@cursor/july 0.1.113 → 0.1.114

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 (229) hide show
  1. package/dist/docs/404.html +2 -2
  2. package/dist/docs/assets/{app.CAeK13eM.js → app.BqkJwOZ-.js} +4 -4
  3. package/dist/docs/assets/chunks/@localSearchIndexroot.BnSgidYE.js +1 -0
  4. package/dist/docs/assets/chunks/{VPLocalSearchBox.C9LbPHod.js → VPLocalSearchBox.BJAi2KiV.js} +1 -1
  5. package/dist/docs/assets/chunks/{arc.CmMq2zmS.js → arc.BZpXTgvV.js} +1 -1
  6. package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.CCXB8Uj5.js → architectureDiagram-Q4EWVU46.WYI-7F-Y.js} +1 -1
  7. package/dist/docs/assets/chunks/{baseUniq.CyQo6eLe.js → baseUniq.CZaUPpg0.js} +1 -1
  8. package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.JYq6w91N.js → blockDiagram-DXYQGD6D.D6UES2pD.js} +1 -1
  9. package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.BRV8GPJJ.js → c4Diagram-AHTNJAMY.cwebIe4i.js} +1 -1
  10. package/dist/docs/assets/chunks/channel.DdM5EfNW.js +1 -0
  11. package/dist/docs/assets/chunks/{chunk-4BX2VUAB.Bv4ooYQR.js → chunk-4BX2VUAB.fVyFnjxg.js} +1 -1
  12. package/dist/docs/assets/chunks/{chunk-4TB4RGXK.t4JtKPcj.js → chunk-4TB4RGXK.BanufG1c.js} +1 -1
  13. package/dist/docs/assets/chunks/{chunk-55IACEB6.34lCHj9Y.js → chunk-55IACEB6.VaSMz5-2.js} +1 -1
  14. package/dist/docs/assets/chunks/{chunk-EDXVE4YY.BSwrPNrt.js → chunk-EDXVE4YY.CN2diZOM.js} +1 -1
  15. package/dist/docs/assets/chunks/{chunk-FMBD7UC4.Beeun-R-.js → chunk-FMBD7UC4.g4ivypu3.js} +1 -1
  16. package/dist/docs/assets/chunks/{chunk-OYMX7WX6.BUUFUcJc.js → chunk-OYMX7WX6.GZXKn9JJ.js} +1 -1
  17. package/dist/docs/assets/chunks/{chunk-QZHKN3VN.B2XjHzN_.js → chunk-QZHKN3VN.itXxJZCd.js} +1 -1
  18. package/dist/docs/assets/chunks/{chunk-YZCP3GAM.CLYG8znk.js → chunk-YZCP3GAM.-rw2GfvX.js} +1 -1
  19. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.CjfGHeg2.js +1 -0
  20. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.CjfGHeg2.js +1 -0
  21. package/dist/docs/assets/chunks/clone.wSOICb_f.js +1 -0
  22. package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.DVEa6fZp.js → cose-bilkent-S5V4N54A.CmaI5br0.js} +1 -1
  23. package/dist/docs/assets/chunks/{dagre-KV5264BT.C9PZQK-S.js → dagre-KV5264BT.4wY9S4Kt.js} +1 -1
  24. package/dist/docs/assets/chunks/{diagram-5BDNPKRD.DoN0uv3Y.js → diagram-5BDNPKRD.Pc3c0u9W.js} +1 -1
  25. package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.Czv3duqx.js → diagram-G4DWMVQ6.CYrWz-nj.js} +1 -1
  26. package/dist/docs/assets/chunks/{diagram-MMDJMWI5.BinJ5kWb.js → diagram-MMDJMWI5.Bgj5hukb.js} +1 -1
  27. package/dist/docs/assets/chunks/{diagram-TYMM5635.DW326M4K.js → diagram-TYMM5635.DGMEXalS.js} +1 -1
  28. package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.U2pR_OA7.js → erDiagram-SMLLAGMA.GepTV9Im.js} +1 -1
  29. package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.ByWJXeYK.js → flowDiagram-DWJPFMVM.DVKywg3j.js} +1 -1
  30. package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.OquF0Rtg.js → ganttDiagram-T4ZO3ILL.C7qt9Mlo.js} +1 -1
  31. package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.Bpn01P7X.js → gitGraphDiagram-UUTBAWPF.U30_r82P.js} +1 -1
  32. package/dist/docs/assets/chunks/{graph.CNRB6ETL.js → graph.CyyMyAWv.js} +1 -1
  33. package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.CqhknMWi.js → infoDiagram-42DDH7IO.Dn9ACW3y.js} +1 -1
  34. package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.C6xpR2af.js → ishikawaDiagram-UXIWVN3A.DlIdIGOA.js} +1 -1
  35. package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.Cg5f7oB3.js → journeyDiagram-VCZTEJTY.DZj4vy4E.js} +1 -1
  36. package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.Cx9YTwlU.js → kanban-definition-6JOO6SKY.Dl63eMUV.js} +1 -1
  37. package/dist/docs/assets/chunks/{layout.ljS-wFtK.js → layout.BLHZLWPH.js} +1 -1
  38. package/dist/docs/assets/chunks/{linear.jSxNrsFC.js → linear.aXKGKaNw.js} +1 -1
  39. package/dist/docs/assets/chunks/{min.Cum8AlQw.js → min.zWnFcpcc.js} +1 -1
  40. package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.BLiysLpe.js → mindmap-definition-QFDTVHPH.Qs4MQBea.js} +1 -1
  41. package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.BoIDyuKF.js → pieDiagram-DEJITSTG.BmPHgsk7.js} +1 -1
  42. package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.DLkpDytR.js → quadrantDiagram-34T5L4WZ.D5MQ3gwA.js} +1 -1
  43. package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.DqTVqSu2.js → requirementDiagram-MS252O5E.CkdUFrO7.js} +1 -1
  44. package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.CG_6FF7j.js → sankeyDiagram-XADWPNL6.KZrljrAV.js} +1 -1
  45. package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.BIp9602K.js → sequenceDiagram-FGHM5R23.XMoEW-Lx.js} +1 -1
  46. package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.COSXsD9I.js → stateDiagram-FHFEXIEX.BmTzePLj.js} +1 -1
  47. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.Cu5X28zZ.js +1 -0
  48. package/dist/docs/assets/chunks/{theme.CXJ7PNwy.js → theme.BfQzpxsg.js} +2 -2
  49. package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.CXdVqkLq.js → timeline-definition-GMOUNBTQ.Dug0oamp.js} +1 -1
  50. package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.CZxGuc4r.js → vennDiagram-DHZGUBPP.BOTHrEFu.js} +1 -1
  51. package/dist/docs/assets/chunks/{wardley-RL74JXVD.3oVgfqQk.js → wardley-RL74JXVD.DXy2i1LS.js} +1 -1
  52. package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.6_irCgGJ.js → wardleyDiagram-NUSXRM2D.CoXKdfi6.js} +1 -1
  53. package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.TRPe92m3.js → xychartDiagram-5P7HB3ND.DXoSCjAW.js} +1 -1
  54. package/dist/docs/assets/{deployment.md.D2jQZuFx.js → deployment.md.D2YX7u_I.js} +1 -1
  55. package/dist/docs/assets/{guides_agent-to-agent.md.CD4T5FIl.js → guides_agent-to-agent.md.C6kPY8nu.js} +2 -2
  56. package/dist/docs/assets/{guides_cloud-agents.md.Cp1O3u-X.js → guides_cloud-agents.md.BPJqTZjT.js} +1 -1
  57. package/dist/docs/assets/{guides_grokbot-agents.md.CMhZNdEU.js → guides_grokbot-agents.md.CzV715v8.js} +1 -1
  58. package/dist/docs/assets/guides_hooks.md.BT9GLwEp.js +50 -0
  59. package/dist/docs/assets/guides_hooks.md.BT9GLwEp.lean.js +1 -0
  60. package/dist/docs/assets/{guides_jev.md.F5fAkkfN.js → guides_jev.md.DeSCqMaO.js} +6 -44
  61. package/dist/docs/assets/guides_jev.md.DeSCqMaO.lean.js +1 -0
  62. package/dist/docs/assets/reference_agent-config.md.BRxAlnRy.js +36 -0
  63. package/dist/docs/assets/{reference_agent-config.md.DGPyw7ms.lean.js → reference_agent-config.md.BRxAlnRy.lean.js} +1 -1
  64. package/dist/docs/assets/reference_artifacts.md.KRX0sAdt.js +18 -0
  65. package/dist/docs/assets/reference_artifacts.md.KRX0sAdt.lean.js +1 -0
  66. package/dist/docs/assets/reference_channels.md.DZr14vm7.js +23 -0
  67. package/dist/docs/assets/reference_channels.md.DZr14vm7.lean.js +1 -0
  68. package/dist/docs/assets/{reference_connections.md.Je9dMsdd.js → reference_connections.md.DJGUCxrr.js} +18 -30
  69. package/dist/docs/assets/{reference_connections.md.Je9dMsdd.lean.js → reference_connections.md.DJGUCxrr.lean.js} +1 -1
  70. package/dist/docs/assets/{reference_evals.md.DNJzM_yf.js → reference_evals.md.C6umwNC6.js} +6 -7
  71. package/dist/docs/assets/reference_evals.md.C6umwNC6.lean.js +1 -0
  72. package/dist/docs/assets/{reference_extensions.md.Cv5aLCz_.js → reference_extensions.md.DbNYu-DP.js} +3 -3
  73. package/dist/docs/assets/{reference_extensions.md.Cv5aLCz_.lean.js → reference_extensions.md.DbNYu-DP.lean.js} +1 -1
  74. package/dist/docs/assets/reference_hooks.md.BfOkhTU0.js +45 -0
  75. package/dist/docs/assets/{reference_hooks.md.B7uzNENk.lean.js → reference_hooks.md.BfOkhTU0.lean.js} +1 -1
  76. package/dist/docs/assets/reference_http-api.md.DdwtBeCj.js +11 -0
  77. package/dist/docs/assets/{reference_http-api.md.CduHavZ2.lean.js → reference_http-api.md.DdwtBeCj.lean.js} +1 -1
  78. package/dist/docs/assets/reference_instructions.md.B2mcIzT6.js +14 -0
  79. package/dist/docs/assets/reference_instructions.md.B2mcIzT6.lean.js +1 -0
  80. package/dist/docs/assets/reference_playground.md.CyrQD_n3.js +1 -0
  81. package/dist/docs/assets/reference_playground.md.CyrQD_n3.lean.js +1 -0
  82. package/dist/docs/assets/reference_project-layout.md.BEMzxAkq.js +19 -0
  83. package/dist/docs/assets/{reference_project-layout.md.BGhgpy9V.lean.js → reference_project-layout.md.BEMzxAkq.lean.js} +1 -1
  84. package/dist/docs/assets/reference_prompt.md.BFrqjHFL.js +9 -0
  85. package/dist/docs/assets/reference_prompt.md.BFrqjHFL.lean.js +1 -0
  86. package/dist/docs/assets/reference_schedules.md.BB9N3tRR.js +47 -0
  87. package/dist/docs/assets/reference_schedules.md.BB9N3tRR.lean.js +1 -0
  88. package/dist/docs/assets/reference_sessions.md.BBp-GIt-.js +1 -0
  89. package/dist/docs/assets/{reference_sessions.md.1_6Vyv7x.lean.js → reference_sessions.md.BBp-GIt-.lean.js} +1 -1
  90. package/dist/docs/assets/reference_skills.md.BVmi3UJ_.js +15 -0
  91. package/dist/docs/assets/{reference_skills.md.DjQkRefx.lean.js → reference_skills.md.BVmi3UJ_.lean.js} +1 -1
  92. package/dist/docs/assets/reference_subagents.md.DRoRy2Uj.js +10 -0
  93. package/dist/docs/assets/{reference_subagents.md.Dl16gcBj.lean.js → reference_subagents.md.DRoRy2Uj.lean.js} +1 -1
  94. package/dist/docs/assets/{reference_tools.md.B1dH1lpa.js → reference_tools.md.CgocLDX1.js} +9 -6
  95. package/dist/docs/assets/{reference_tools.md.B1dH1lpa.lean.js → reference_tools.md.CgocLDX1.lean.js} +1 -1
  96. package/dist/docs/assets/troubleshooting.md.HY95rCCz.js +1 -0
  97. package/dist/docs/building-with-agents.html +35 -35
  98. package/dist/docs/deployment.html +37 -37
  99. package/dist/docs/deployment.md +1 -1
  100. package/dist/docs/evals.html +35 -35
  101. package/dist/docs/guides/agent-to-agent.html +38 -38
  102. package/dist/docs/guides/agent-to-agent.md +11 -12
  103. package/dist/docs/guides/bitbucket.html +35 -35
  104. package/dist/docs/guides/cloud-agents.html +36 -36
  105. package/dist/docs/guides/cloud-agents.md +1 -1
  106. package/dist/docs/guides/convert-automation.html +35 -35
  107. package/dist/docs/guides/github.html +35 -35
  108. package/dist/docs/guides/gitlab.html +35 -35
  109. package/dist/docs/guides/grokbot-agents.html +37 -37
  110. package/dist/docs/guides/grokbot-agents.md +1 -1
  111. package/dist/docs/guides/hooks.html +109 -0
  112. package/dist/docs/guides/hooks.md +111 -0
  113. package/dist/docs/guides/improve.html +35 -35
  114. package/dist/docs/guides/jev.html +42 -80
  115. package/dist/docs/guides/jev.md +22 -79
  116. package/dist/docs/guides/mcp-oauth.html +36 -36
  117. package/dist/docs/guides/opentelemetry.html +35 -35
  118. package/dist/docs/guides/slack.html +35 -35
  119. package/dist/docs/guides/webhooks.html +35 -35
  120. package/dist/docs/hashmap.json +1 -1
  121. package/dist/docs/hillclimbing.html +35 -35
  122. package/dist/docs/index.html +35 -35
  123. package/dist/docs/llms-full.txt +993 -1303
  124. package/dist/docs/llms.txt +8 -7
  125. package/dist/docs/quickstart.html +35 -35
  126. package/dist/docs/reference/agent-config.html +42 -46
  127. package/dist/docs/reference/agent-config.md +48 -81
  128. package/dist/docs/reference/artifacts.html +39 -40
  129. package/dist/docs/reference/artifacts.md +71 -70
  130. package/dist/docs/reference/channels.html +41 -61
  131. package/dist/docs/reference/channels.md +134 -201
  132. package/dist/docs/reference/cli.html +35 -35
  133. package/dist/docs/reference/connections.html +54 -66
  134. package/dist/docs/reference/connections.md +92 -128
  135. package/dist/docs/reference/evals.html +42 -43
  136. package/dist/docs/reference/evals.md +42 -50
  137. package/dist/docs/reference/extensions.html +38 -38
  138. package/dist/docs/reference/extensions.md +9 -13
  139. package/dist/docs/reference/hooks.html +39 -67
  140. package/dist/docs/reference/hooks.md +72 -146
  141. package/dist/docs/reference/http-api.html +39 -39
  142. package/dist/docs/reference/http-api.md +137 -161
  143. package/dist/docs/reference/instructions.html +39 -39
  144. package/dist/docs/reference/instructions.md +21 -36
  145. package/dist/docs/reference/playground.html +36 -36
  146. package/dist/docs/reference/playground.md +26 -43
  147. package/dist/docs/reference/project-layout.html +38 -38
  148. package/dist/docs/reference/project-layout.md +12 -17
  149. package/dist/docs/reference/prompt.html +42 -42
  150. package/dist/docs/reference/prompt.md +18 -13
  151. package/dist/docs/reference/schedules.html +56 -91
  152. package/dist/docs/reference/schedules.md +52 -99
  153. package/dist/docs/reference/sessions.html +36 -36
  154. package/dist/docs/reference/sessions.md +36 -40
  155. package/dist/docs/reference/skills.html +38 -38
  156. package/dist/docs/reference/skills.md +15 -26
  157. package/dist/docs/reference/subagents.html +38 -38
  158. package/dist/docs/reference/subagents.md +20 -30
  159. package/dist/docs/reference/tools.html +44 -41
  160. package/dist/docs/reference/tools.md +45 -64
  161. package/dist/docs/templates/agentic-owners.html +35 -35
  162. package/dist/docs/templates/pr-autofixer.html +35 -35
  163. package/dist/docs/templates/security-reviewer.html +35 -35
  164. package/dist/docs/templates/thermo-quality-review.html +35 -35
  165. package/dist/docs/templates/thermo-review.html +35 -35
  166. package/dist/docs/templates/triage.html +35 -35
  167. package/dist/docs/troubleshooting.html +36 -36
  168. package/dist/docs/troubleshooting.md +1 -1
  169. package/dist/playground/assets/{index-DSMAewbx.css → index-C61EWMBK.css} +1 -1
  170. package/dist/playground/index.html +2 -2
  171. package/docs/deployment.md +1 -1
  172. package/docs/guides/agent-to-agent.md +11 -12
  173. package/docs/guides/cloud-agents.md +1 -1
  174. package/docs/guides/grokbot-agents.md +1 -1
  175. package/docs/guides/hooks.md +116 -0
  176. package/docs/guides/jev.md +23 -80
  177. package/docs/reference/agent-config.md +48 -81
  178. package/docs/reference/artifacts.md +72 -71
  179. package/docs/reference/channels.md +135 -202
  180. package/docs/reference/connections.md +93 -129
  181. package/docs/reference/evals.md +43 -51
  182. package/docs/reference/extensions.md +9 -13
  183. package/docs/reference/hooks.md +72 -146
  184. package/docs/reference/http-api.md +137 -161
  185. package/docs/reference/instructions.md +22 -37
  186. package/docs/reference/playground.md +26 -43
  187. package/docs/reference/project-layout.md +12 -17
  188. package/docs/reference/prompt.md +20 -15
  189. package/docs/reference/schedules.md +52 -99
  190. package/docs/reference/sessions.md +36 -40
  191. package/docs/reference/skills.md +15 -26
  192. package/docs/reference/subagents.md +20 -30
  193. package/docs/reference/tools.md +45 -64
  194. package/docs/troubleshooting.md +1 -1
  195. package/package.json +1 -1
  196. package/dist/docs/assets/chunks/@localSearchIndexroot.Ck9E52Ls.js +0 -1
  197. package/dist/docs/assets/chunks/channel.BHiYmnZ4.js +0 -1
  198. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.Degh8l90.js +0 -1
  199. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.Degh8l90.js +0 -1
  200. package/dist/docs/assets/chunks/clone.BIywbczV.js +0 -1
  201. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.qrxrbFsX.js +0 -1
  202. package/dist/docs/assets/guides_jev.md.F5fAkkfN.lean.js +0 -1
  203. package/dist/docs/assets/reference_agent-config.md.DGPyw7ms.js +0 -40
  204. package/dist/docs/assets/reference_artifacts.md.Bu_4HmsD.js +0 -19
  205. package/dist/docs/assets/reference_artifacts.md.Bu_4HmsD.lean.js +0 -1
  206. package/dist/docs/assets/reference_channels.md.nFWbzAic.js +0 -43
  207. package/dist/docs/assets/reference_channels.md.nFWbzAic.lean.js +0 -1
  208. package/dist/docs/assets/reference_evals.md.DNJzM_yf.lean.js +0 -1
  209. package/dist/docs/assets/reference_hooks.md.B7uzNENk.js +0 -73
  210. package/dist/docs/assets/reference_http-api.md.CduHavZ2.js +0 -11
  211. package/dist/docs/assets/reference_instructions.md.CU1My5My.js +0 -14
  212. package/dist/docs/assets/reference_instructions.md.CU1My5My.lean.js +0 -1
  213. package/dist/docs/assets/reference_playground.md.Ch2d0Iqi.js +0 -1
  214. package/dist/docs/assets/reference_playground.md.Ch2d0Iqi.lean.js +0 -1
  215. package/dist/docs/assets/reference_project-layout.md.BGhgpy9V.js +0 -19
  216. package/dist/docs/assets/reference_prompt.md.Ccp0R53H.js +0 -1
  217. package/dist/docs/assets/reference_prompt.md.Ccp0R53H.lean.js +0 -1
  218. package/dist/docs/assets/reference_schedules.md.B2Nm6FaD.js +0 -82
  219. package/dist/docs/assets/reference_schedules.md.B2Nm6FaD.lean.js +0 -1
  220. package/dist/docs/assets/reference_sessions.md.1_6Vyv7x.js +0 -1
  221. package/dist/docs/assets/reference_skills.md.DjQkRefx.js +0 -15
  222. package/dist/docs/assets/reference_subagents.md.Dl16gcBj.js +0 -10
  223. package/dist/docs/assets/troubleshooting.md.mnfFG2Em.js +0 -1
  224. /package/dist/docs/assets/{deployment.md.D2jQZuFx.lean.js → deployment.md.D2YX7u_I.lean.js} +0 -0
  225. /package/dist/docs/assets/{guides_agent-to-agent.md.CD4T5FIl.lean.js → guides_agent-to-agent.md.C6kPY8nu.lean.js} +0 -0
  226. /package/dist/docs/assets/{guides_cloud-agents.md.Cp1O3u-X.lean.js → guides_cloud-agents.md.BPJqTZjT.lean.js} +0 -0
  227. /package/dist/docs/assets/{guides_grokbot-agents.md.CMhZNdEU.lean.js → guides_grokbot-agents.md.CzV715v8.lean.js} +0 -0
  228. /package/dist/docs/assets/{troubleshooting.md.mnfFG2Em.lean.js → troubleshooting.md.HY95rCCz.lean.js} +0 -0
  229. /package/dist/playground/assets/{index-De_lpFxE.js → index-CrMWlgUU.js} +0 -0
@@ -331,7 +331,7 @@ per-caller and channel-specific auth.
331
331
 
332
332
  Use `agent-sdk logs --prod` for runtime output,
333
333
  [OpenTelemetry](/docs/guides/opentelemetry.md) for traces and metrics, and
334
- [session traces](/docs/reference/sessions.md#how-do-i-inspect-a-saved-event-stream)
334
+ [session traces](/docs/reference/sessions.md#inspect-a-saved-event-stream)
335
335
  for one conversation.
336
336
 
337
337
  ## Related
@@ -682,8 +682,8 @@ await ctx.host.mcp.callTool("weather", "check", {
682
682
  });
683
683
  ```
684
684
 
685
- The peer owns this session. Follow-ups stay on the specialist and leave
686
- the concierge's conversation unchanged.
685
+ The peer owns this session, so follow-ups continue the specialist's
686
+ context.
687
687
 
688
688
  ## Call a deterministic peer tool
689
689
 
@@ -728,14 +728,13 @@ complete agent you would also run, inspect, or expose on its own.
728
728
 
729
729
  ## Affinity
730
730
 
731
- Follow-up `ask` and `check` calls resume the same specialist session
732
- and run as the caller. Those turns show up in the peer's playground;
733
- only that caller can continue or check the session.
731
+ Those turns show up in the peer's playground. Only the session owner
732
+ can continue or check them.
734
733
 
735
734
  ## Practices
736
735
 
737
- - Give each caller a one-way routing rule so agent A cannot send work
738
- to B and receive the same request back.
736
+ - Give each caller a one-way routing rule, and don't configure reciprocal
737
+ routes for the same request.
739
738
 
740
739
  - Prefer `ask` when the specialist should reason. Use `call_tool` when
741
740
  host code already knows which server tool to run.
@@ -746,12 +745,12 @@ only that caller can continue or check the session.
746
745
 
747
746
  ## Other hosts
748
747
 
749
- Peers need the default multi-agent layout and one shared process;
750
- unknown slugs and self-references fail at startup.
748
+ Peers require the default multi-agent layout; unknown slugs and
749
+ self-references fail at startup.
751
750
 
752
- For a self-hosted caller, expose the shared process with `--public-url`
753
- and protect it with `--bearer-token`. The
754
- [Deployment guide](/docs/deployment.md) owns that hosting setup.
751
+ If a cloud-runtime turn needs a self-hosted peer, set `--public-url` to
752
+ the shared host's reachable URL and protect it with `--bearer-token`.
753
+ The [Deployment guide](/docs/deployment.md) owns that hosting setup.
755
754
 
756
755
  ## Related
757
756
 
@@ -1064,7 +1063,7 @@ agent, keep talking while it works, inspect the result, and steer or
1064
1063
  stop the same branch.
1065
1064
 
1066
1065
  This is different from choosing
1067
- [`runtime: "cloud"`](/docs/reference/agent-config.md#choose-a-runtime).
1066
+ [`runtime: "cloud"`](/docs/reference/agent-config.md#runtime).
1068
1067
  That setting moves this agent's turns to a cloud VM; this extension lets
1069
1068
  the agent delegate separate coding tasks.
1070
1069
 
@@ -2161,11 +2160,127 @@ directory and disable that tool. See
2161
2160
  overlays
2162
2161
  - [Tool approvals](/docs/reference/tools.md#gate-a-tool-on-human-approval):
2163
2162
  approve asks
2164
- - [Agent config](/docs/reference/agent-config.md#choose-a-runtime): local
2163
+ - [Agent config](/docs/reference/agent-config.md#runtime): local
2165
2164
  and hosted runtimes
2166
2165
 
2167
2166
  ---
2168
2167
 
2168
+ Source: /docs/guides/hooks.md
2169
+
2170
+ # Hooks
2171
+
2172
+ Hooks observe session events after they are recorded and run side effects such
2173
+ as updating metrics or sending alerts. They never change the turn, prompt, or
2174
+ reply. Author them under `agent/hooks/` with `defineHook` from
2175
+ `@cursor/july/hooks`.
2176
+
2177
+ ## Meter token usage
2178
+
2179
+ With [OpenTelemetry](/docs/guides/opentelemetry.md) configured, this hook adds a live
2180
+ turn's reported input and output tokens to your counters. Eval runs stay quiet,
2181
+ so the dashboard reflects live traffic instead of the test suite.
2182
+
2183
+ ```ts
2184
+ // agent/hooks/usage.ts
2185
+ import { defineHook } from "@cursor/july/hooks";
2186
+
2187
+ export default defineHook({
2188
+ events: {
2189
+ async "turn.completed"(event, ctx) {
2190
+ if (ctx.session.purpose === "eval") {
2191
+ return;
2192
+ }
2193
+ if (event.data.usage === undefined) {
2194
+ return;
2195
+ }
2196
+
2197
+ const { inputTokens, outputTokens } = event.data.usage;
2198
+ ctx.host.otel.increment("acme.tokens.input", inputTokens);
2199
+ ctx.host.otel.increment("acme.tokens.output", outputTokens);
2200
+ },
2201
+ },
2202
+ });
2203
+ ```
2204
+
2205
+ ## Alert on failure
2206
+
2207
+ Set `PAGER_WEBHOOK_URL` to your pager's webhook. When a live turn fails, this
2208
+ hook posts the agent, session, and failure message there. Eval runs and
2209
+ interrupted turns stay quiet, so tests and preemptions do not page anyone.
2210
+
2211
+ ```ts
2212
+ // agent/hooks/page-on-failure.ts
2213
+ import { defineHook } from "@cursor/july/hooks";
2214
+
2215
+ export default defineHook({
2216
+ events: {
2217
+ async "turn.failed"(event, ctx) {
2218
+ if (ctx.session.purpose === "eval") {
2219
+ return;
2220
+ }
2221
+ if (event.data.message === "turn interrupted") {
2222
+ return;
2223
+ }
2224
+
2225
+ const pagerUrl = process.env.PAGER_WEBHOOK_URL;
2226
+ if (pagerUrl === undefined) {
2227
+ return;
2228
+ }
2229
+
2230
+ await fetch(pagerUrl, {
2231
+ method: "POST",
2232
+ headers: { "content-type": "application/json" },
2233
+ body: JSON.stringify({
2234
+ agent: ctx.agent.name,
2235
+ session: ctx.session.id,
2236
+ channel: ctx.channel.id,
2237
+ message: event.data.message,
2238
+ }),
2239
+ signal: AbortSignal.timeout(5_000),
2240
+ });
2241
+ },
2242
+ },
2243
+ });
2244
+ ```
2245
+
2246
+ ## Events / when hooks run
2247
+
2248
+ Use event names from the
2249
+ [session event vocabulary](/docs/reference/sessions.md#stream-events). A hook
2250
+ receives each matching event after it is recorded, and the model does not wait
2251
+ for the handler.
2252
+
2253
+ Within one session, handlers run one at a time. A slow handler delays later
2254
+ handlers for that session, but it does not delay the model or handlers for
2255
+ other sessions. Hooks also fire for evals, so check
2256
+ `ctx.session.purpose === "eval"` before metering or paging. A restart does not
2257
+ replay recorded events into hooks.
2258
+
2259
+ ## When not to use a hook
2260
+
2261
+ | Want | Use instead |
2262
+ | --- | --- |
2263
+ | Add context before the model | `instructions.md`, skills, or `workspaceFiles` |
2264
+ | Deliver to Slack or a PR | Channel [`events`](/docs/reference/channels.md#events) or packs |
2265
+ | Block or approve a tool | [`needsApproval`](/docs/reference/tools.md#gate-a-tool-on-human-approval) |
2266
+ | Gate final assistant text | `defineResult` |
2267
+ | Gate behavior | [Evals](/docs/evals.md) |
2268
+
2269
+ [Cursor Agent hooks](https://cursor.com/docs/agent/hooks) in
2270
+ `.cursor/hooks.json` are a different product. They can observe, block, or
2271
+ modify the local agent loop.
2272
+
2273
+ ## Related
2274
+
2275
+ - [Hooks reference](/docs/reference/hooks.md): payloads, context, and discovery
2276
+ - [Sessions: stream events](/docs/reference/sessions.md#stream-events): event
2277
+ vocabulary and payload sequence
2278
+ - [OpenTelemetry](/docs/guides/opentelemetry.md): export traces and custom metrics
2279
+ - [Channels: events](/docs/reference/channels.md#events): deliver replies back to
2280
+ Slack, source control, or another surface
2281
+
2282
+ ---
2283
+
2169
2284
  Source: /docs/guides/improve.md
2170
2285
 
2171
2286
  # Self-improvement
@@ -2319,15 +2434,11 @@ Source: /docs/guides/jev.md
2319
2434
 
2320
2435
  # Use Jev in review tools
2321
2436
 
2322
- Use Jev for narrow decisions where your software factory needs a typed
2323
- answer, not another paragraph. A review agent can filter speculative
2324
- findings, classify pull request risk, choose a reviewer, or decide
2325
- whether a merge needs a documentation follow-up. Jev returns a choice,
2326
- score, or probability; your TypeScript decides what happens next.
2327
-
2328
- Ask one question per judgment. If an approval depends on risk, test
2329
- coverage, and the size of the change, ask three questions in one call
2330
- and combine the answers in code.
2437
+ Use Jev when a review workflow needs a typed decision instead of prose.
2438
+ A review agent can filter speculative findings, classify pull request
2439
+ risk, choose a reviewer, or decide whether a merge needs a documentation
2440
+ follow-up. Jev returns a choice, score, or probability; your TypeScript
2441
+ decides what happens next.
2331
2442
 
2332
2443
  Set `TYPESAFE_API_KEY`. The default model is `jev-latest`.
2333
2444
 
@@ -2340,11 +2451,12 @@ export default jev();
2340
2451
 
2341
2452
  ## Start only the review turns you need
2342
2453
 
2343
- You can call Jev from a channel hook before a model turn starts. When a
2344
- pull request opens, this hook reads its title, labels, and filenames,
2345
- then asks whether the change needs security review. A high probability
2346
- starts the review. Otherwise the hook returns `null`, so the agent does
2347
- not run and nothing is posted to GitHub.
2454
+ Call Jev from a channel hook before a model turn starts. When a pull
2455
+ request opens or becomes ready for review, this hook sends its title,
2456
+ body, labels, and filenames to Jev to decide whether the change needs
2457
+ security review. A result that clears the threshold starts the review
2458
+ turn; otherwise, the hook returns `null`, so the agent doesn't run or
2459
+ post to GitHub.
2348
2460
 
2349
2461
  ```ts
2350
2462
  // agent/channels/github.ts
@@ -2400,8 +2512,7 @@ export default githubChannel({
2400
2512
  });
2401
2513
  ```
2402
2514
 
2403
- Only pull request metadata goes to Jev here. The review turn still reads
2404
- the diff itself.
2515
+ Jev receives only the pull request metadata shown here, not the diff.
2405
2516
 
2406
2517
  ## Filter findings before you post them
2407
2518
 
@@ -2506,8 +2617,8 @@ approval event.
2506
2617
 
2507
2618
  After a pull request merges, a code-wiki agent can ask whether the
2508
2619
  change introduced a durable fact that belongs in the docs. A low
2509
- probability means no follow-up. A high probability opens a documentation
2510
- pull request instead of turning every merge into churn.
2620
+ probability skips the follow-up, while a high probability opens a
2621
+ documentation pull request.
2511
2622
 
2512
2623
  ```ts
2513
2624
  import { above, decide } from "@cursor/july/extensions/jev";
@@ -2532,14 +2643,14 @@ return openDocsPullRequest({ title, summary });
2532
2643
 
2533
2644
  ## Write tools with Jev
2534
2645
 
2535
- `decide` is a regular async host function. Call it from a channel hook,
2536
- server tool, or router. It returns the answer map directly. `evaluate`
2537
- makes the same request and returns `{ answers }`.
2646
+ Call `decide` from a channel hook, server tool, or router when you want
2647
+ the answer map directly. Use `evaluate` when you want `{ answers }`.
2538
2648
 
2539
2649
  Use a boolean for a yes-or-no gate, a choice for a closed set such as
2540
- risk tiers or owners, and a score for an ordered rubric. `above` checks
2541
- a boolean probability or score. `needsHuman` checks whether a boolean
2542
- or the selected choice clears your confidence bar.
2650
+ risk tiers or owners, and a score for an ordered rubric. `above` returns
2651
+ `true` when a boolean probability or score meets the threshold.
2652
+ `needsHuman` returns `true` when a boolean probability or the selected
2653
+ choice's probability falls below the confidence threshold.
2543
2654
 
2544
2655
  Keep the questions atomic and combine them in TypeScript. For example,
2545
2656
  ask separately whether a finding is real, whether its impact is
@@ -2549,8 +2660,8 @@ rule that decides whether all three are enough to post.
2549
2660
  ## Let the agent ask Jev
2550
2661
 
2551
2662
  Mounting the extension adds a read-only harness tool named
2552
- `<namespace>__evaluate`. With the default `jev` filename, the model sees
2553
- `jev__evaluate`.
2663
+ `<namespace>__evaluate`. With the `agent/extensions/jev.ts` mount shown
2664
+ earlier, the model sees `jev__evaluate`.
2554
2665
 
2555
2666
  The tool accepts one state and a list of boolean, choice, or score
2556
2667
  questions. It returns `{ answers }` and never posts, approves, or opens
@@ -2592,59 +2703,6 @@ export default jev({
2592
2703
  the exported helpers, so project tools can still import `decide`,
2593
2704
  `above`, and `needsHuman`.
2594
2705
 
2595
- ## Jev and trajectory evals
2596
-
2597
- An eval checks a whole turn: the agent called the read, and it stayed
2598
- off approve. `decide` is the one call inside the tool, before GitHub.
2599
- Use the [Evals guide](/docs/evals.md) when you want the turn to keep
2600
- behaving. Use `decide` when the tool itself needs a tier or a yes.
2601
-
2602
- ## Test without the live API
2603
-
2604
- Pass `fetch` and `apiKey` and the same `decide` path runs in a test.
2605
-
2606
- ```ts
2607
- import { decide } from "@cursor/july/extensions/jev";
2608
-
2609
- const answers = await decide({
2610
- state: {
2611
- title: "Skip the refund auth check",
2612
- summary: "acme/checkout#42 drops the session check on POST /refunds.",
2613
- },
2614
- questions: {
2615
- risk: {
2616
- type: "choice",
2617
- instructions: "What risk tier is this pull request?",
2618
- criteria: {
2619
- "very-low": "docs, formatting, or a mechanical rename",
2620
- low: "a local change with tests and no new trust boundary",
2621
- medium: "auth, billing, or a behavior change callers depend on",
2622
- high: "a likely exploit, data loss, or a broken public contract",
2623
- },
2624
- },
2625
- },
2626
- apiKey: "sk-test",
2627
- fetch: async () =>
2628
- new Response(
2629
- JSON.stringify({
2630
- answers: {
2631
- risk: {
2632
- type: "choice",
2633
- choice: "high",
2634
- probabilities: {
2635
- "very-low": 0.02,
2636
- low: 0.05,
2637
- medium: 0.18,
2638
- high: 0.75,
2639
- },
2640
- },
2641
- },
2642
- }),
2643
- { status: 200 }
2644
- ),
2645
- });
2646
- ```
2647
-
2648
2706
  ## Practices
2649
2707
 
2650
2708
  - Pass the pull request title, a short summary, and the draft finding.
@@ -4032,11 +4090,11 @@ coding agent extend the project for you
4032
4090
 
4033
4091
  Source: /docs/reference/agent-config.md
4034
4092
 
4035
- # Agent config (`agent/agent.ts`)
4093
+ # Agent config
4036
4094
 
4037
- `agent/agent.ts` default-exports `defineAgent(config)`: which model runs
4038
- the agent, where turns execute, and runtime-specific defaults.
4039
- Everything is optional on the root agent.
4095
+ `agent/agent.ts` default-exports `defineAgent(config)`, which sets the
4096
+ model, execution runtime, and runtime-specific defaults. Every root
4097
+ config field is optional.
4040
4098
 
4041
4099
  ```ts
4042
4100
  import { defineAgent } from "@cursor/july";
@@ -4056,9 +4114,7 @@ export default defineAgent({
4056
4114
  });
4057
4115
  ```
4058
4116
 
4059
- ## Fields on `defineAgent`
4060
-
4061
- `defineAgent` accepts these fields.
4117
+ ## Agent fields
4062
4118
 
4063
4119
  | Field | Type | Meaning |
4064
4120
  | --- | --- | --- |
@@ -4067,14 +4123,14 @@ export default defineAgent({
4067
4123
  | `description` | string | What the agent is for. Required on subagents; the parent model reads it to decide when to delegate. Documentation-only on the root. |
4068
4124
  | `instructions` | string | Inline instructions. Prefer `instructions.md`; this exists for subagents and generated configs. |
4069
4125
  | `runtime` | `"local"` or `"cloud"` | Where turns execute. Default `"local"`. |
4070
- | `cloud` | object | Cloud agent defaults: repos, env, envVars, forwarded to the Cursor SDK. Used when `runtime` is `"cloud"`, and as the base merged under per-session `cloud` send options. |
4126
+ | `cloud` | object | Cloud agent defaults: repos, env, envVars. Used when `runtime` is `"cloud"`, and as the base merged under per-session `cloud` send options. |
4071
4127
  | `local` | `{ cwd?, workspaceDir?, sandbox? }` | Local harness defaults; ignored for cloud turns. See [Local options](#local-options). |
4072
- | `hosting` | `{ egressDomains?, secretNames? }` | Managed-hosting declarations read by `agent-sdk deploy`: the pod's egress allowlist and the secret names the agent expects. Ignored by local serving. |
4073
- | `concurrency` | `{ maxRunningTurns? }` | Engine-wide turn admission limit. See [Concurrency](#concurrency). |
4128
+ | `hosting` | `{ egressDomains?, secretNames? }` | `agent-sdk deploy` declarations for allowed egress domains and expected secret names. Ignored by local serving. |
4129
+ | `concurrency` | `{ maxRunningTurns? }` | Agent-wide turn admission limit. See [Concurrency](#concurrency). |
4074
4130
  | `builtinTools` | `{ reminders? }` | Framework-provided model-facing tools, opted in per capability. See [Built-in tools](#built-in-tools). |
4075
- | `tools` | `ToolName[]` | Allowlist of built-in harness tools offered to the model. Unset = the model's full standard toolset. See [Allowlist built-in harness tools](#allowlist-built-in-harness-tools). |
4131
+ | `tools` | `ToolName[]` | Allowlist of built-in harness tools offered to the model. Unset = the model's full standard toolset. See [Harness tools](#harness-tools). |
4076
4132
 
4077
- ## Choose a model
4133
+ ## Model
4078
4134
 
4079
4135
  `model` is a Cursor model id string, or `{ id, params }`. Effort and
4080
4136
  speed are params, not id suffixes. The SDK rejects suffix-style ids
@@ -4096,24 +4152,17 @@ A plain string works when you don't need params:
4096
4152
  model: "composer-2.5",
4097
4153
  ```
4098
4154
 
4099
- ## Choose a runtime
4155
+ ## Runtime
4100
4156
 
4101
- `runtime: "local"` (the default) runs turns on the Cursor SDK harness on
4102
- this machine. Server tools, skills, sandbox seeds, and tool approvals
4103
- all apply.
4157
+ `runtime: "local"` (the default) runs turns on this machine. Server
4158
+ tools, skills, sandbox seeds, and tool approvals all apply.
4104
4159
 
4105
- `runtime: "cloud"` runs turns on Cursor cloud agents (`bc-…` ids). Pass
4106
- a `cloud` block with the repos the VM carries. Server tools stay
4107
- reachable over authenticated HTTP MCP back to the serve host when
4108
- `--public-url` or `--cloud-tools-url` is set (omitted with a warning
4109
- otherwise), and instructions and agent-tool catalogs are prepended to
4110
- the first prompt, because the local session workspace is not the cloud
4111
- VM.
4160
+ `runtime: "cloud"` runs turns on Cursor cloud agents. Pass a `cloud`
4161
+ block with the repositories the VM needs. See [Tools](/docs/reference/tools.md) for
4162
+ server- and agent-tool behavior on cloud turns.
4112
4163
 
4113
- `validate` warns when `runtime: "cloud"` is combined with agent tools
4114
- (they are described on the first prompt instead of written to the VM),
4115
- when skills or sandbox seeds are present (they sync onto an Agent Store
4116
- rather than the session workspace), and when the `cloud` block is
4164
+ `validate` warns when `runtime: "cloud"` is combined with agent tools,
4165
+ when skills or sandbox seeds are present, and when the `cloud` block is
4117
4166
  missing.
4118
4167
 
4119
4168
  ## Local options
@@ -4122,18 +4171,15 @@ missing.
4122
4171
 
4123
4172
  `local.workspaceDir` points every session at one shared harness cwd,
4124
4173
  for agents that work inside an existing checkout. It takes precedence
4125
- over `cwd`, and a per-send `workspaceDir` still wins over both. The SDK
4126
- keys its local executor (rules, skills, MCP, ignore mappings) on the
4127
- harness cwd, so a shared directory resolves the workspace once per
4128
- serve process instead of once per session. The trade: sessions share a
4129
- working tree, so a file one turn writes is visible to the next.
4174
+ over `cwd`, and a per-send `workspaceDir` still wins over both.
4175
+ Sessions share a working tree, so a file one turn writes is visible to
4176
+ the next.
4130
4177
 
4131
4178
  `local.sandbox` runs the harness inside Cursor's local sandbox. It's
4132
- off by default, matching the SDK: shell then auto-approves and inherits
4133
- the serve process environment, including any credentials the host
4134
- holds. Turn it on for agents whose turns read untrusted input (webhook
4135
- payloads, PR diffs, inbound chat); it's a real tool boundary rather
4136
- than a prompt-level one.
4179
+ off by default: shell then auto-approves and inherits the serve process
4180
+ environment, including any credentials the host holds. Turn it on for
4181
+ agents whose turns read untrusted input (webhook payloads, PR diffs,
4182
+ inbound chat); it's a tool boundary, not a prompt-level one.
4137
4183
 
4138
4184
  ### Local cwd
4139
4185
 
@@ -4149,13 +4195,12 @@ does not leak rules, skills, or MCP servers into the turn. A standalone git
4149
4195
  root keeps the in-project session workspace. Point `cwd` at a checkout only
4150
4196
  when the agent should inherit that tree.
4151
4197
 
4152
- ## Allowlist built-in harness tools
4198
+ ## Harness tools
4153
4199
 
4154
4200
  Use `tools` to limit which built-in Cursor harness tools the model can
4155
4201
  call. Omit it to keep the standard toolset. When you set it, the model
4156
4202
  gets only the tools you list. An empty list disables all native
4157
- built-in tools. Because this field is an allowlist, new platform tools
4158
- stay disabled until you add them.
4203
+ built-in tools. New platform tools stay disabled until you add them.
4159
4204
 
4160
4205
  ```ts
4161
4206
  export default defineAgent({
@@ -4166,12 +4211,13 @@ export default defineAgent({
4166
4211
  });
4167
4212
  ```
4168
4213
 
4169
- The Agent SDK always adds `"mcp"` to a configured allowlist. Authored
4170
- server tools in `agent/tools/` use MCP to reach the model. MCP can also
4171
- expose declared connections and servers from the harness directory's
4172
- ambient `.cursor` config. To exclude a checkout's MCP servers, point
4173
- `local.cwd` outside the checkout. See [Local cwd](#local-cwd).
4174
- `local.sandbox` makes MCP tool calls fail closed.
4214
+ The Agent SDK always adds `"mcp"` to a configured allowlist, because
4215
+ authored server tools in `agent/tools/` reach the model over MCP. MCP
4216
+ can also expose declared connections and servers from the harness
4217
+ directory's ambient `.cursor` config. To exclude a checkout's MCP
4218
+ servers, point `local.cwd` outside the checkout. See
4219
+ [Local cwd](#local-cwd). `local.sandbox` makes MCP tool calls fail
4220
+ closed.
4175
4221
 
4176
4222
  Use the SDK's public tool names, including `"shell"`, `"read"`,
4177
4223
  `"edit"`, `"grep"`, `"glob"`, `"ls"`, and `"task"`. Unknown names
@@ -4188,8 +4234,7 @@ Two names have broader effects:
4188
4234
  Tool allowlists work only with the local runtime. A
4189
4235
  `runtime: "cloud"` agent that sets `tools` fails at serve startup.
4190
4236
  The Agent SDK also refuses per-send cloud sessions from a hybrid agent
4191
- with an allowlist. It won't run those sessions with unrestricted tool
4192
- access.
4237
+ with an allowlist.
4193
4238
 
4194
4239
  The allowlist controls which tools the model can call. It does not
4195
4240
  isolate the serve host. For agents that process untrusted input, also
@@ -4197,10 +4242,10 @@ set `local: { sandbox: true }`.
4197
4242
 
4198
4243
  ## Cloud options
4199
4244
 
4200
- Cloud agent defaults forwarded to the Cursor SDK: `repos` (each
4201
- `{ url, startingRef? }`), environment selection, `envVars`, and the
4202
- rest. A local agent uses the same block as the base config when a
4203
- channel opens a cloud-attached session per send (the `cloud` option on
4245
+ The `cloud` block sets default repositories (each
4246
+ `{ url, startingRef? }`), environment selection, and `envVars`. A local
4247
+ agent uses the same block as the base config when a channel opens a
4248
+ cloud-attached session per send (the `cloud` option on
4204
4249
  [`send`](/docs/reference/channels.md#handler-arguments)).
4205
4250
 
4206
4251
  ## Concurrency
@@ -4223,9 +4268,8 @@ export default defineAgent({
4223
4268
  ## Built-in tools
4224
4269
 
4225
4270
  `builtinTools` opts into framework-provided model-facing tools. Each
4226
- enabled capability materializes as ordinary server tools at discovery
4227
- time, so turns, direct calls, `info`, and the playground treat them
4228
- like authored tools. Authored tools with the same name win, with a
4271
+ enabled capability shows up as ordinary server tools, so turns, direct
4272
+ calls, `info`, and the playground treat them like authored tools. Authored tools with the same name win, with a
4229
4273
  warning, and like all server tools they run on the local runtime.
4230
4274
 
4231
4275
  `builtinTools: { reminders: true }` adds three tools bound to the
@@ -4234,22 +4278,6 @@ current conversation over `host.reminders`: `reminders_create`,
4234
4278
  continuation key can't arm reminders. See
4235
4279
  [Schedules and reminders](/docs/reference/schedules.md#reminders).
4236
4280
 
4237
- ## Generate instructions
4238
-
4239
- When the system prompt must be computed, author `agent/instructions.ts`
4240
- instead of markdown:
4241
-
4242
- ```ts
4243
- import { defineInstructions } from "@cursor/july";
4244
-
4245
- export default defineInstructions({
4246
- markdown: `You are the on-call assistant for ${process.env.TEAM_NAME}.`,
4247
- });
4248
- ```
4249
-
4250
- The directory form and the runtime mapping are in
4251
- [Instructions](/docs/reference/instructions.md).
4252
-
4253
4281
  ## Serve programmatically
4254
4282
 
4255
4283
  `serve(dirOrProject, options)` embeds the server in your own process:
@@ -4259,7 +4287,7 @@ import { serve } from "@cursor/july";
4259
4287
 
4260
4288
  const handle = await serve("./my-agent", {
4261
4289
  port: 3000,
4262
- apiKey: process.env.CURSOR_API_KEY, // optional; see credential order
4290
+ apiKey: process.env.CURSOR_API_KEY,
4263
4291
  });
4264
4292
  console.log(`listening on ${handle.url}`);
4265
4293
  // handle.callTool(...), handle.dispatchSchedule("heartbeat"),
@@ -4268,20 +4296,17 @@ console.log(`listening on ${handle.url}`);
4268
4296
 
4269
4297
  Host settings match the documented [CLI](/docs/reference/cli.md) `serve` flags.
4270
4298
  `serve()` also accepts `discovery` (project-loading options) and
4271
- `mode: "single" | "multi"`. The Cursor credential resolves in one order
4272
- everywhere: explicit `apiKey`, then `CURSOR_API_KEY`, then
4273
- `CURSOR_API_KEY_FILE` (hosted default `/run/cursor/secrets/CURSOR_API_KEY`
4274
- when unset), then `CURSOR_SERVICE_ACCOUNT_KEY`, then the key stored by
4275
- `agent-sdk login`. On a host that has both the service-account key and a
4276
- bind file, the file principal wins.
4299
+ `mode: "single" | "multi"`. Pass `apiKey` or use the same Cursor
4300
+ credential as the CLI: `CURSOR_API_KEY`, `CURSOR_API_KEY_FILE`,
4301
+ `CURSOR_SERVICE_ACCOUNT_KEY`, or `agent-sdk login`.
4277
4302
 
4278
- ## What's next
4279
-
4280
- Continue with these pages:
4303
+ ## Related
4281
4304
 
4282
4305
  - [Instructions](/docs/reference/instructions.md): the required half of a minimal
4283
4306
  agent
4284
4307
  - [CLI](/docs/reference/cli.md): the `serve` flags `serve()` accepts
4308
+ - [Sessions](/docs/reference/sessions.md): workspaces, identifiers, and turn admission
4309
+ - [Schedules](/docs/reference/schedules.md): reminder tools opted in here
4285
4310
 
4286
4311
  ---
4287
4312
 
@@ -4289,13 +4314,12 @@ Source: /docs/reference/artifacts.md
4289
4314
 
4290
4315
  # Artifacts
4291
4316
 
4292
- An artifact marks a durable output the agent produced: a reviewed PR
4293
- URL, a generated report, a decision record. Sessions come and go;
4294
- artifacts persist across them, capped and listable, so the people
4295
- supervising an agent see what it shipped without replaying event
4296
- streams.
4317
+ Artifacts are durable outputs such as reviewed pull requests, reports,
4318
+ or decision records. An artifact kind defines the data it accepts, and
4319
+ the artifacts API tags or updates records by key. Records persist across
4320
+ sessions and can be listed, streamed, or downloaded.
4297
4321
 
4298
- ## Declare kinds
4322
+ ## Artifact kinds
4299
4323
 
4300
4324
  Author `agent/artifacts.ts` with `defineArtifacts` from
4301
4325
  `@cursor/july/artifacts`:
@@ -4316,26 +4340,21 @@ export default defineArtifacts({
4316
4340
  });
4317
4341
  ```
4318
4342
 
4319
- `defineArtifacts` accepts three fields. `kinds` declares the artifact
4320
- kinds: with kinds declared, `tag` accepts only these; with none, any
4321
- kind string is accepted freeform. Each kind's `description` says what it
4322
- holds and doubles as the model-facing prompt for `tag_artifact`. An
4323
- optional Zod `schema` validates payloads before they persist (the parsed
4324
- output is stored, so defaults and coercions apply). `agentTool` exposes
4325
- the model-facing `tag_artifact` tool generated from the kinds registry;
4326
- it requires at least one declared kind. `max` is the retention cap,
4327
- default 1000: on insert past the cap, the oldest-updated artifact is
4328
- evicted.
4343
+ | Option | Contract |
4344
+ | --- | --- |
4345
+ | `kinds` | Map of accepted kind names to a non-empty `description` and optional Zod `schema` |
4346
+ | `agentTool` | Expose `tag_artifact` to the model; requires at least one declared kind |
4347
+ | `max` | Positive retention cap; defaults to `1000` and evicts the oldest-updated record |
4348
+
4349
+ With declared kinds, `tag` rejects any other kind. With no registry, it
4350
+ accepts free-form kind names and defaults an omitted kind to
4351
+ `"artifact"`. A kind's schema validates `data`, and the parsed value is
4352
+ stored, including schema defaults and coercions.
4329
4353
 
4330
- ## Tag from host code
4354
+ ## Tag artifacts from host code
4331
4355
 
4332
- Every handler surface carries `ctx.artifacts` (or `args.artifacts`),
4333
- an `ArtifactsApi` with `tag` and `list`: tools, hooks, channel route
4334
- handlers and `onStart`, schedule `run` handlers, and reminder `run`
4335
- handlers. Tool and hook facades are session-bound, so `tag` auto-fills
4336
- the `sessionId` (and `turnId` when known). Channel, schedule, and
4337
- reminder facades are unbound; pass `sessionId` in the tag input to
4338
- attribute one.
4356
+ Tools, hooks, channel handlers, channel `onStart`, schedules, and
4357
+ reminders receive an `ArtifactsApi` with `tag` and `list`.
4339
4358
 
4340
4359
  ```ts
4341
4360
  await ctx.artifacts.tag({
@@ -4346,59 +4365,66 @@ await ctx.artifacts.tag({
4346
4365
  });
4347
4366
  ```
4348
4367
 
4349
- `key` is the upsert handle: tagging the same key again replaces the
4350
- record instead of creating a new one, so re-reviewing a PR updates one
4351
- row. A `contents` payload (string or bytes, with an optional
4352
- `contentType`) attaches a file or blob served at
4353
- `GET /v1/artifacts/:id/content`; re-tagging a keyed artifact without
4354
- `contents` keeps the existing payload.
4368
+ | Tag field | Contract |
4369
+ | --- | --- |
4370
+ | `data` | Required JSON data, validated when the kind has a schema |
4371
+ | `kind` | Declared or free-form kind |
4372
+ | `key` | Agent-wide upsert key; the same key updates one record across kinds |
4373
+ | `title` | Optional display title |
4374
+ | `contents` | String or bytes served by the content route |
4375
+ | `contentType` | MIME type for `contents` |
4376
+ | `sessionId`, `turnId` | Attribute the artifact to a session or turn |
4377
+ | `source` | `"host"` or `"model"`; defaults to `"host"` |
4378
+
4379
+ Tool, hook, result, and channel-event contexts are session-bound, so
4380
+ they fill `sessionId` and the current `turnId`. Channel routes,
4381
+ `onStart`, schedules, and reminders receive an unbound facade; pass
4382
+ `sessionId` to attribute an artifact.
4383
+
4384
+ Re-tagging a key without `contents` keeps its file or blob when the
4385
+ `sessionId` stays the same. Rebinding the key to another session without
4386
+ new contents removes the previous payload. `tag` returns the stored
4387
+ `ArtifactRecord`.
4388
+
4389
+ | Record field | Contract |
4390
+ | --- | --- |
4391
+ | `id` | Stable ID derived from `key`, or a generated ID when no key is set |
4392
+ | `kind`, `data` | Validated kind and JSON payload |
4393
+ | `key`, `title` | Optional upsert key and display title |
4394
+ | `content` | Optional `{ size, contentType? }` metadata |
4395
+ | `sessionId`, `turnId` | Optional session attribution |
4396
+ | `source` | `"host"` or `"model"` |
4397
+ | `createdAt`, `updatedAt` | ISO-8601 timestamps |
4355
4398
 
4356
- ## Let the model tag
4399
+ ## Expose `tag_artifact` to the model
4357
4400
 
4358
4401
  With `agentTool: true`, the `tag_artifact` server tool materializes from
4359
- the kinds registry. Its description tells the model to tag notable
4360
- outputs and lists each kind with its description, and its input schema
4361
- is a discriminated union over the declared kinds, so a schema'd kind is
4362
- validated exactly like a host-side tag. An authored tool named
4363
- `tag_artifact` shadows the built-in, with a warning.
4364
-
4365
- ## Observe and list
4402
+ the kinds registry. Its input accepts the declared kinds and validates
4403
+ their data with the same schemas as host-side tagging. The kind
4404
+ descriptions tell the model which output each one represents.
4366
4405
 
4367
- Tagging emits an `artifact.tagged` event on the attributed session's
4368
- stream, carrying the record: `id`, `kind`, `key`, `title`, `data`, and
4369
- `source` (`"host"` for host code, `"model"` for `tag_artifact`). Hooks,
4370
- channel `events`, and evals see it like any other
4371
- [stream event](/docs/reference/sessions.md#which-events-can-i-stream).
4406
+ An authored tool named `tag_artifact` takes precedence over the generated
4407
+ tool.
4372
4408
 
4373
- Over HTTP:
4409
+ ## List artifacts
4374
4410
 
4375
- ```bash
4376
- curl 'http://127.0.0.1:3000/<slug>/v1/artifacts?kind=reviewed-pr&limit=20'
4377
- curl 'http://127.0.0.1:3000/<slug>/v1/artifacts/<id>/content'
4378
- ```
4379
-
4380
- `GET /v1/artifacts` returns records newest-updated first, filterable by
4381
- `kind` and `sessionId`. Session ownership applies, same as
4382
- `/v1/sessions`. The playground renders tagged artifacts too.
4411
+ `list({ kind?, sessionId? })` returns matching records newest-updated
4412
+ first. HTTP callers can list records and download content through the
4413
+ [artifact routes](/docs/reference/http-api.md#list-and-download-artifacts).
4383
4414
 
4384
- ## Gate evals on tagging
4415
+ ## Stream artifact tags
4385
4416
 
4386
- `t.taggedArtifact(kind?, predicate?)` gates an eval on at least one
4387
- artifact tagged during the test turn, optionally of one kind and
4388
- matching a predicate over the record:
4389
-
4390
- ```ts
4391
- t.taggedArtifact("reviewed-pr", (record) => record.source === "model");
4392
- ```
4417
+ Tagging an artifact with a `sessionId` emits `artifact.tagged` on that
4418
+ session. Its event data contains `id`, `kind`, `key`, `title`, `data`,
4419
+ and `source`. See [Stream events](/docs/reference/sessions.md#stream-events) for the
4420
+ event envelope.
4393
4421
 
4394
- ## What's next
4395
-
4396
- Continue with these pages:
4422
+ ## Related
4397
4423
 
4398
- - [Sessions and streaming](/docs/reference/sessions.md): the `artifact.tagged` event
4399
- in the full vocabulary
4400
- - [Tools](/docs/reference/tools.md): the `ctx` that carries `artifacts`
4401
- - [Evals](/docs/evals.md): the assertions `taggedArtifact` sits beside
4424
+ - [Sessions](/docs/reference/sessions.md)
4425
+ - [Tools](/docs/reference/tools.md)
4426
+ - [Evals](/docs/reference/evals.md)
4427
+ - [HTTP API](/docs/reference/http-api.md)
4402
4428
 
4403
4429
  ---
4404
4430
 
@@ -4406,24 +4432,21 @@ Source: /docs/reference/channels.md
4406
4432
 
4407
4433
  # Channels
4408
4434
 
4409
- A channel is the surface an agent lives on. The built-in HTTP session
4410
- channel is always mounted. Custom channels declare their own routes
4411
- under `/v1/channels/<id>`. The Slack and GitHub packs are prebuilt
4412
- channels with platform transports. GitLab and Bitbucket packs cover their
4413
- hosted and self-managed products. This page is the authoring reference;
4414
- for the walkthrough, see the [Webhooks guide](/docs/guides/webhooks.md).
4435
+ A channel connects an external surface to agent sessions. A file at
4436
+ `agent/channels/<id>.ts` defines channel `<id>` and mounts its routes
4437
+ under `/v1/channels/<id>`. The built-in HTTP session channel is always
4438
+ available alongside any custom or prebuilt channels.
4415
4439
 
4416
4440
  ## Built-in HTTP channel
4417
4441
 
4418
- It's always mounted, under `/<slug>` in the default multi-agent layout:
4419
- session create, follow-up, stream, stop, the sessions list, approvals,
4420
- deterministic tool calls, health, and info. For the route-by-route
4421
- contract, see the [HTTP API reference](/docs/reference/http-api.md).
4442
+ The built-in channel serves the session, approval, tool, discovery, and
4443
+ health routes. In a multi-agent host, each agent's routes sit under its
4444
+ slug. See the [HTTP API](/docs/reference/http-api.md) for request and response
4445
+ contracts.
4422
4446
 
4423
4447
  ## Define a custom channel
4424
4448
 
4425
- Author `agent/channels/<id>.ts`. The filename is the channel id and the
4426
- route prefix:
4449
+ Use `defineChannel` with one or more typed routes:
4427
4450
 
4428
4451
  ```ts
4429
4452
  import { defineChannel, POST } from "@cursor/july/channels";
@@ -4436,237 +4459,173 @@ export default defineChannel({
4436
4459
  bodySchema: z.object({
4437
4460
  message: z.string(),
4438
4461
  prUrl: z.string().url(),
4439
- thread: z.string().optional(),
4440
4462
  }),
4441
- handler: async (_req, { callTool, send, body }) => {
4442
- const prepared = await callTool("inspect_pr", {
4443
- prUrl: body.prUrl,
4444
- });
4445
- if (prepared.isError) {
4446
- return Response.json(
4447
- { error: prepared.errorMessage ?? "Could not inspect pull request" },
4448
- { status: 502 }
4449
- );
4450
- }
4451
-
4463
+ handler: async (_request, { send, body }) => {
4452
4464
  const session = await send(
4453
- `${body.message}\n\nRead pr.json before answering.`,
4465
+ `${body.message}\n\nPull request: ${body.prUrl}`,
4454
4466
  {
4455
- continuationToken: body.thread ?? `pr:${body.prUrl}`,
4456
- workspaceFiles: {
4457
- "pr.json": JSON.stringify(prepared.result, null, 2) ?? "null",
4458
- },
4467
+ continuationToken: `pr:${body.prUrl}`,
4459
4468
  }
4460
4469
  );
4461
4470
  return Response.json({ sessionId: session.id });
4462
4471
  },
4463
4472
  }),
4464
4473
  ],
4465
- events: {
4466
- "message.completed"(event, channel, ctx) {
4467
- // deliver the reply to the surface that owns this channel
4468
- },
4469
- },
4470
- // auth: [...], state: {...}, onStart(...), onStop(...)
4471
4474
  });
4472
4475
  ```
4473
4476
 
4474
- This example assumes `agent/tools/inspect_pr.ts` exists. The handler
4475
- calls it before the model turn, so every review starts with validated PR
4476
- data. It also derives a stable conversation key from the PR URL and
4477
- writes the tool result to `pr.json`. Instructions can ask the model to
4478
- inspect a PR, but host code guarantees it.
4477
+ The route above is
4478
+ `POST /v1/channels/<id>/review`. Calls for the same pull request reuse
4479
+ one conversation because they pass the same continuation token.
4479
4480
 
4480
4481
  ## Route verbs and schemas
4481
4482
 
4482
- `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` helpers build routes. Their
4483
- schemas are Zod, enforced at compile time:
4483
+ Route paths must begin with `/`. The method helper determines which Zod
4484
+ schemas the route accepts:
4484
4485
 
4485
- | Verb | Required schema |
4486
- | ------------------------ | ------------------------------------- |
4487
- | `GET` | `querySchema` |
4488
- | `POST` / `PUT` / `PATCH` | `bodySchema` (optional `querySchema`) |
4489
- | `DELETE` | both optional |
4486
+ | Helper | Schema contract |
4487
+ | --- | --- |
4488
+ | `GET` | `querySchema` is required |
4489
+ | `POST`, `PUT`, `PATCH` | `bodySchema` is required; `querySchema` is optional |
4490
+ | `DELETE` | Both schemas are optional |
4490
4491
 
4491
- Plain JSON Schema objects won't type-check; use `z.object({})` or
4492
- `z.unknown()` for intentionally open surfaces. The host validates before
4493
- the handler runs. Handlers receive typed `args.body` and `args.query`,
4494
- and empty POST bodies are coerced to `{}` first. Declared schemas are
4495
- projected on `GET /v1/info`, which powers the playground's **Try**
4496
- buttons and composer **slash commands**.
4492
+ Plain JSON Schema objects don't type-check. Use `z.object({})` or
4493
+ `z.unknown()` for an open surface. The host validates the body and query
4494
+ before calling the handler, returning `400` on failure; an empty body is
4495
+ read as `{}`. Declared schemas also appear in `GET /v1/info` for
4496
+ playground requests and slash commands.
4497
4497
 
4498
4498
  ## Handler arguments
4499
4499
 
4500
- Handlers receive the Fetch `Request` and an args object:
4501
-
4502
- | Member | What it is |
4503
- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
4504
- | `send(message, options?)` | Run a model turn on this channel; returns the session handle (options below) |
4505
- | `getSession(sessionId)` | Look up an existing session on this channel |
4506
- | `receive(channelDefinition, input)` | Hand off to another channel (schedules use this) |
4507
- | `callTool(name, input, options?)` | Deterministic server-tool call ([Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn)) |
4508
- | `body`, `query`, `params` | Validated payloads and `:param` path segments |
4509
- | `auth` | The `AuthContext` resolved by this route's auth chain |
4510
- | `requestIp` | The TCP peer address |
4511
- | `host` | Shared services: `host.mcp`, `host.github`, `host.slack`, `host.kv`, `host.files`, `host.reminders` |
4512
- | `waitUntil(promise)` | Background work that outlives the response |
4513
- | `sessionUrls(request, sessionId)` | Absolute playground + trace URLs for a session on this mount |
4514
- | `artifacts` | Unbound [artifacts](/docs/reference/artifacts.md) facade; pass `sessionId` in `tag` input to attribute one |
4515
-
4516
- `send` options: `continuationToken` (the conversation key),
4517
- `admission` (`"preempt"` interrupts a busy session, the default;
4518
- `"coalesce"` enqueues behind the running turn, the
4519
- [Slack policy](/docs/reference/sessions.md#what-happens-when-i-send-a-follow-up)),
4520
- `workspaceFiles`, `workspaceDir`, `cloud` (attach cloud repos for this
4521
- session), `auth` (defaults to the request principal), `state` (starting
4522
- channel state for new sessions), `title` (session display title), and
4523
- `purpose` (`"eval"` marks the session as regression traffic).
4500
+ Each handler receives the Fetch `Request` and a typed arguments object.
4501
+
4502
+ | Member | Contract |
4503
+ | --- | --- |
4504
+ | `send(message, options?)` | Start or resume a session on this channel |
4505
+ | `getSession(sessionId)` | Return this channel's session, or `null` |
4506
+ | `receive(channel, input)` | Hand work to another channel |
4507
+ | `callTool(name, input, options?)` | [Call a server tool](/docs/reference/tools.md#call-a-tool-without-a-model-turn) without a model turn |
4508
+ | `body`, `query`, `params` | Validated inputs and `:param` path segments |
4509
+ | `auth` | The `AuthContext` returned by the route's auth chain |
4510
+ | `requestIp` | The TCP peer address, or `null` |
4511
+ | `host` | Shared MCP, provider, storage, telemetry, and reminder services |
4512
+ | `waitUntil(promise)` | Track work after the response returns |
4513
+ | `sessionUrls(request, sessionId)` | Build absolute playground and trace URLs for this mount |
4514
+ | `artifacts` | List or tag [artifacts](/docs/reference/artifacts.md); pass `sessionId` when attributing one |
4515
+
4516
+ ### `send` options
4517
+
4518
+ | Option | Contract |
4519
+ | --- | --- |
4520
+ | `continuationToken` | Resume the session with this channel-local key, or create one when the key is new |
4521
+ | `admission` | `"preempt"` interrupts a busy turn; `"coalesce"` queues behind it. The default is `"preempt"` |
4522
+ | `workspaceFiles` | Add relative files for the next turn |
4523
+ | `workspaceDir` | Use an absolute local working directory |
4524
+ | `cloud` | Override cloud session options when creating a session |
4525
+ | `auth` | Set the session principal; defaults to the request principal |
4526
+ | `state` | Set starting channel state for a new session |
4527
+ | `title` | Set the display title for a new session |
4528
+ | `purpose` | Use `"eval"` to mark a new session as regression traffic |
4529
+ | `dryRun` | Run read tools and stub write tools for a new session |
4530
+ | `asOf` | Freeze a new session at an ISO-8601 instant with a timezone |
4531
+
4532
+ `send` returns a `ChannelSession`. Its `id` identifies the session,
4533
+ `continuationToken` contains its current channel key, and `isNew` says
4534
+ whether this call created it. A coalesced call also returns
4535
+ `coalesced: true`.
4524
4536
 
4525
4537
  ## Events
4526
4538
 
4527
4539
  The `events` map subscribes the channel to stream events for the
4528
4540
  sessions it owns. Keys are event types from the
4529
- [event vocabulary](/docs/reference/sessions.md#which-events-can-i-stream), or `"*"`.
4530
- Handlers receive `(event, channel, ctx)`, where `channel.state` is the
4531
- per-session adapter state, `ctx.session` is the session info, and
4532
- `ctx.host` is the shared host services, bound to that session as in a
4533
- [hook](/docs/reference/hooks.md#handler-context). This is where a channel delivers
4534
- replies back to its surface.
4535
-
4536
- ## State and lifecycle
4537
-
4538
- `state` declares the starting per-session adapter state (JSON), persisted
4539
- on the session record. Routes and event handlers read and mutate it
4540
- through `channel.state`. `onStart(args)` runs when the channel mounts.
4541
- `onStop()` runs when the server stops.
4542
-
4543
- `onStart` receives the route helpers (`send`, `getSession`, `receive`,
4544
- `callTool`, `host`, `waitUntil`, `artifacts`, `logger`) plus helpers
4545
- for long-lived transports:
4546
-
4547
- - `emitAssistantMessage(sessionId, text)` appends an assistant message
4548
- without a model turn, for host tasks that already produced the final
4549
- text.
4550
- - `hasContinuationSession(token)` and `isContinuationBusy(token)`
4551
- report whether a continuation token has a live session and whether a
4552
- turn is in flight on it.
4553
- - `interruptContinuation(token)` stops the in-flight turn and clears
4554
- coalesced follow-ups queued behind it.
4555
- - `resolveApproval(sessionId, callId, decision, auth, options?)`
4556
- approves or denies a parked tool call, how Slack Block Kit buttons
4557
- unblock a turn without the HTTP approvals route.
4541
+ [event vocabulary](/docs/reference/sessions.md#stream-events), or `"*"`. Each handler
4542
+ receives `(event, channel, ctx)`, including the session's
4543
+ `channel.state`, session info, and shared host services. Use these
4544
+ handlers to deliver progress and replies to the surface that owns the
4545
+ channel.
4558
4546
 
4559
- ## Auth policies
4547
+ ## Session state
4548
+
4549
+ `state` on the channel definition supplies starting JSON for each new
4550
+ session. A route can instead pass `state` to `send`; event handlers read
4551
+ and update the active value through `channel.state`.
4560
4552
 
4561
- Every route runs an auth-policy chain: the channel's `auth` array, or
4562
- `[localDevStrict()]` when unset. A policy is a function
4563
- `(request, info) => AuthContext | null` (async allowed); the first
4564
- non-null wins, and a request no policy admits gets `401`.
4553
+ ## Start and stop a channel
4565
4554
 
4566
- | Policy | Admits |
4567
- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
4568
- | `localDevStrict()` | Direct loopback callers with no proxy-forwarding headers (`X-Forwarded-For`, `X-Real-IP`, `Forwarded`, and `X-Forwarded-Host` are all rejected, so tunnels and same-host reverse proxies don't silently re-expose the route), plus a loopback `Host` header, which rejects DNS-rebinding callers that reach 127.0.0.1 with a remote hostname. |
4569
- | `localDev()` | Like `localDevStrict()` but without the `Host` check. An explicit, weaker opt-in. |
4570
- | `loopbackOnly()` | A loopback TCP peer, ignoring forwarding headers; for dev relays that legitimately carry them, like `gh webhook forward`. |
4571
- | `bearerAuth(token)` | `Authorization: Bearer <token>`, compared in constant time. Also accepts a verifier function mapping a presented token to an `AuthContext`. |
4572
- | `allowAll()` | Everyone, as an `anonymous` principal. Only for surfaces protected upstream (an HMAC-verified webhook) or intentionally public. |
4573
- | `publicEndpoint()` | Everyone on this custom channel. Managed hosting also serves the channel without an alias token. Use it only when the handler verifies the provider signature. |
4555
+ `onStart(args)` runs when the channel mounts, and `onStop()` runs when
4556
+ the host stops. `onStart` receives the route helpers plus these
4557
+ transport controls:
4574
4558
 
4575
- `publicEndpoint()` applies only to custom channel routes. It does not open
4576
- the built-in session or tool API.
4559
+ | Helper | Contract |
4560
+ | --- | --- |
4561
+ | `emitAssistantMessage(sessionId, text)` | Append final assistant text without starting a model turn |
4562
+ | `hasContinuationSession(token)` | Check whether a token maps to a session |
4563
+ | `isContinuationBusy(token)` | Check whether a turn is active for a token |
4564
+ | `interruptContinuation(token)` | Stop the active turn and clear queued coalesced follow-ups |
4565
+ | `resolveApproval(...)` | Approve or deny a parked tool call |
4566
+
4567
+ ## Auth policies
4568
+
4569
+ Every route runs the channel's `auth` array. The default is
4570
+ `[localDevStrict()]`. Policies may be asynchronous; the first one to
4571
+ return an `AuthContext` admits the request, and an all-null result
4572
+ returns `401`.
4573
+
4574
+ | Policy | Admits |
4575
+ | --- | --- |
4576
+ | `localDevStrict()` | Direct loopback requests with a loopback hostname and no proxy-forwarding headers |
4577
+ | `localDev()` | Direct loopback requests with no proxy-forwarding headers, without checking the hostname |
4578
+ | `loopbackOnly()` | Any loopback TCP peer, including local relays that carry forwarding headers |
4579
+ | `bearerAuth(tokenOrVerify)` | A matching bearer token, or a token accepted by the verifier |
4580
+ | `sharedSecretAuth({ header, secret })` | A header matching the named environment or deployment secret |
4581
+ | `hmacSignatureAuth({ header, secret, prefix? })` | A hex HMAC-SHA256 signature over the raw body using the named secret |
4582
+ | `allowAll()` | Every caller as an anonymous principal |
4583
+ | `publicEndpoint()` | Every caller on this custom channel; managed hosting also exposes the route without an alias token |
4584
+
4585
+ Use `allowAll()` only for an intentionally public surface or one
4586
+ protected upstream. With `publicEndpoint()`, the handler must verify the
4587
+ provider's signature. The policy applies only to custom channel routes;
4588
+ it doesn't open the built-in session or tool API.
4577
4589
 
4578
4590
  The resolved `AuthContext` (`{ authenticator, principalId,
4579
4591
  principalType, attributes? }`) becomes the request principal. Sessions
4580
- bind to the principal that created them, and follow-up, stream, and list
4581
- routes enforce ownership (`403` otherwise).
4592
+ belong to the principal that created them, and other principals receive
4593
+ `403` on owned routes.
4582
4594
 
4583
4595
  Server flags interact with authored auth: `--bearer-token` swaps the
4584
4596
  default `localDevStrict()` for `bearerAuth(...)` on channels that don't
4585
4597
  author their own chain, and `--allow-anonymous` swaps it for
4586
- `allowAll()`. Authored `auth` arrays always win over both. A channel that
4587
- declares `[localDevStrict()]` stays loopback-only even on an
4588
- `--allow-anonymous` host.
4589
-
4590
- ## First class channels
4591
-
4592
- **Slack** (`@cursor/july/channels/slack`): Socket Mode
4593
- transport, streaming replies, engagement rules, approval cards, and a
4594
- default block on Slack Connect / guest / other-workspace senders. Author
4595
- `agent/channels/slack.ts` with `slackChannel()`. Guide:
4596
- [Slack](/docs/guides/slack.md).
4597
-
4598
- **GitHub** (`@cursor/july/channels/github`): webhook dispatch
4599
- with signature verification, per-event hooks returning `{ auth }` (a
4600
- model turn), `{ task }` (host work), or `null`, and CLI tooling for
4601
- replay and live forwarding. Author `agent/channels/github.ts` with
4602
- `githubChannel()`. Opt-in `progress.commitStatus` and `progress.banner`
4603
- converge a merge-box check and sticky PR comment from default stream
4604
- events. Supports GitHub.com and GitHub Enterprise Server. Guide:
4605
- [GitHub](/docs/guides/github.md).
4606
-
4607
- **GitLab** (`@cursor/july/channels/gitlab`): verified project hooks for
4608
- merge requests, notes, pipelines, pushes, and custom event types. Supports
4609
- GitLab.com and self-managed GitLab. Author `agent/channels/gitlab.ts` with
4610
- `gitlabChannel()`. Guide: [GitLab](/docs/guides/gitlab.md).
4611
-
4612
- **Bitbucket** (`@cursor/july/channels/bitbucket`): verified repository hooks
4613
- for pull requests, comments, pushes, and custom event types. Supports
4614
- Bitbucket Cloud and Bitbucket Data Center through one normalized hook API.
4615
- Author `agent/channels/bitbucket.ts` with `bitbucketChannel()`. Guide:
4616
- [Bitbucket](/docs/guides/bitbucket.md).
4617
-
4618
- **Deployments** (`@cursor/july/channels/deployments`): pull deploy
4619
- events. Declare `events` and handle each one in `onEvent`. Each event
4620
- carries `deploySourceUri` and `deployVersion`. Author
4621
- `agent/channels/deployments.ts` with `deploymentsChannel()`.
4622
-
4623
- On Cursor-managed hosting, omit `deploySourceUris`. The deployment's
4624
- watched repositories bind the event scope automatically. Name sources
4625
- to narrow the scope or to drive the self-hosted pull relay. Subscribe
4626
- per deploy source with `deploySourceUris` and narrow with `environments`
4627
- or `events`. Each entry must match `Deployment.deploy_source_uri` as
4628
- your deployment writer records it. Matching is case-insensitive but
4629
- otherwise literal. It uses the host credential. A restart resumes
4630
- rather than dropping events. An empty `deploySourceUris` list mounts the
4631
- channel but starts no pull, so an env-configured agent stays inert until
4632
- its deploy sources are set.
4633
-
4634
- **Change Monitors** (`@cursor/july/channels/change-monitors`): Change
4635
- Monitor Checkpoint events. The channel publishes Factory
4636
- `checkpoint.created` for every Checkpoint create. The agent filters the
4637
- result (for example to the `issues` arm). The payload contains the
4638
- full Checkpoint resource. This channel is scoped to Change Monitors,
4639
- not generic Factory Checkpoints. There is no repository filter or
4640
- resource filter. Author `agent/channels/change-monitors.ts` with
4641
- `changeMonitorsChannel()`. It uses the host credential.
4642
-
4643
- **Issues** (`@cursor/july/channels/issues`): Factory issue events.
4644
- The channel publishes `issue.created` for every Issue create. The
4645
- agent filters if it needs a subset. The payload contains the full
4646
- Issue resource. There is no repository filter or resource filter.
4647
- Author `agent/channels/issues.ts` with `issuesChannel()`. It uses the
4648
- host credential.
4598
+ `allowAll()`. An authored `auth` array always takes precedence.
4649
4599
 
4650
- For other platforms like Discord or Teams, use the authored
4651
- `defineChannel` webhook form.
4600
+ ## Prebuilt channels
4652
4601
 
4653
- ## Continuation semantics
4602
+ | Import | Factory | Surface |
4603
+ | --- | --- | --- |
4604
+ | `@cursor/july/channels/slack` | `slackChannel()` | Slack messages, threads, streaming replies, and approvals. See [Slack](/docs/guides/slack.md) |
4605
+ | `@cursor/july/channels/github` | `githubChannel()` | GitHub and GitHub Enterprise webhooks. See [GitHub](/docs/guides/github.md) |
4606
+ | `@cursor/july/channels/gitlab` | `gitlabChannel()` | GitLab.com and self-managed GitLab hooks. See [GitLab](/docs/guides/gitlab.md) |
4607
+ | `@cursor/july/channels/bitbucket` | `bitbucketChannel()` | Bitbucket Cloud and Data Center hooks. See [Bitbucket](/docs/guides/bitbucket.md) |
4608
+ | `@cursor/july/channels/deployments` | `deploymentsChannel()` | Deployment events filtered by source, environment, or event name |
4609
+ | `@cursor/july/channels/change-monitors` | `changeMonitorsChannel()` | Change Monitor `checkpoint.created` events |
4610
+ | `@cursor/july/channels/issues` | `issuesChannel()` | Factory `issue.created` events |
4654
4611
 
4655
- Channels own their continuation-token format. The built-in HTTP channel
4656
- mints opaque rotating tokens, Slack uses `channelId:threadTs`, and PR
4657
- automations use keys like `pr:owner/repo#N`. Same token, same durable
4658
- session; one active continuation per session; the HTTP channel returns
4659
- `409` for stale tokens. For the full session model, see
4660
- [Sessions](/docs/reference/sessions.md).
4612
+ For other platforms like Discord or Teams, use the authored
4613
+ `defineChannel` route form.
4661
4614
 
4662
- ## What's next
4615
+ ## Continuation tokens
4663
4616
 
4664
- Continue with these pages:
4617
+ Each channel defines its continuation-token format. The same token
4618
+ resumes the same conversation; the built-in HTTP channel rotates its
4619
+ opaque token after every accepted follow-up and returns `409` for a
4620
+ stale token. See [Session identifiers](/docs/reference/sessions.md#session-identifiers)
4621
+ for the full contract.
4665
4622
 
4666
- - [Webhooks guide](/docs/guides/webhooks.md): the same API, walked through
4667
- - [HTTP API](/docs/reference/http-api.md): session, discovery, and channel routes
4668
- - [Sessions and streaming](/docs/reference/sessions.md): the events channels
4669
- subscribe to
4623
+ ## Related
4624
+
4625
+ - [Webhooks](/docs/guides/webhooks.md)
4626
+ - [HTTP API](/docs/reference/http-api.md)
4627
+ - [Sessions](/docs/reference/sessions.md)
4628
+ - [Hooks](/docs/reference/hooks.md)
4670
4629
 
4671
4630
  ---
4672
4631
 
@@ -5642,19 +5601,18 @@ These environment variables affect the CLI and its channel packs.
5642
5601
 
5643
5602
  Source: /docs/reference/connections.md
5644
5603
 
5645
- # MCP Connections
5604
+ # MCP connections
5646
5605
 
5647
- An MCP connection gives the agent tools from an MCP server. One file per
5648
- server under `agent/mcp-connections/`, and the filename becomes the server
5649
- name the model sees. An MCP connection default-exports `defineConnection`
5650
- from `@cursor/july/connections`, and the transport comes in
5651
- four shapes: remote HTTP, local stdio, the signed-in Cursor account's
5652
- connectors, and peer agents on the same host.
5606
+ An MCP connection gives the agent tools from an MCP server. Define one
5607
+ file per server under `agent/mcp-connections/`; the filename becomes the
5608
+ server name. Default-export `defineConnection` from
5609
+ `@cursor/july/connections`. The transport is remote HTTP, local stdio,
5610
+ the signed-in Cursor account's connectors, or a peer agent on the same
5611
+ host.
5653
5612
 
5654
- Put a server in `agent/host-connections/` when host tools should call it
5655
- and the model should not. Same `defineConnection` shape. `agent-sdk mcp
5656
- oauth` still works. The playground and the turn's MCP servers never see
5657
- those files.
5613
+ Put a server in `agent/host-connections/` when only host tools should
5614
+ call it. Host connections use the same `defineConnection` shape and
5615
+ support `agent-sdk mcp oauth`; the model and playground don't see them.
5658
5616
 
5659
5617
  ## Remote MCP server
5660
5618
 
@@ -5676,8 +5634,7 @@ Tokens come from env vars. Never hardcode them in the file.
5676
5634
  For servers that speak OAuth, set `oauth: true` and authorize with the
5677
5635
  CLI or mid-run Connect. Tokens live in `mcp-auth.json` under the CLI
5678
5636
  config directory. `--store` copies them onto the deployment as
5679
- `MCP_OAUTH_<NAME>_*` secrets. Hosted Connect lets the current
5680
- process retry.
5637
+ `MCP_OAUTH_<NAME>_*` secrets.
5681
5638
 
5682
5639
  ```ts
5683
5640
  export default defineConnection({
@@ -5689,24 +5646,17 @@ export default defineConnection({
5689
5646
  ```bash
5690
5647
  agent-sdk mcp oauth inventory # browser PKCE → local mcp-auth.json
5691
5648
  agent-sdk mcp oauth inventory --store # also upsert deployment secrets
5692
- # Hosted Connect retries this process. Self-hosted stays file-only.
5693
5649
  ```
5694
5650
 
5695
5651
  Full walkthrough: [Host MCP OAuth](/docs/guides/mcp-oauth.md). Companion
5696
5652
  skill: [`skills/mcp-auth/SKILL.md`](https://github.com/cursor/cursor/blob/main/packages/agent-serve/skills/mcp-auth/SKILL.md).
5697
5653
 
5698
- Account MCP (`cursorAccount: true`) is the right choice for connectors
5699
- already linked in the Cursor dashboard. Omit `servers` (or pass `"*"`)
5700
- to forward every connected connector. If the model should call those
5701
- tools by name on local turns, set `advertiseTools: true`.
5702
-
5703
- ## Per-session auth (`auth`)
5654
+ ## Per-session auth
5704
5655
 
5705
- For http/sse connections whose credential depends on **who the session is
5706
- for** (a multi-tenant agent asserting the tenant it is acting for),
5707
- declare an `auth` callback instead of static headers. It runs host-side
5708
- at turn-build time with the session's `SessionInfo` and returns headers
5709
- merged over the static ones:
5656
+ For http/sse connections whose credential depends on who the session is
5657
+ for, declare an `auth` callback instead of static headers. It runs
5658
+ host-side with the session's `SessionInfo` and returns headers merged
5659
+ over the static ones:
5710
5660
 
5711
5661
  ```ts
5712
5662
  export default defineConnection({
@@ -5714,39 +5664,31 @@ export default defineConnection({
5714
5664
  auth: async (session) => ({
5715
5665
  headers: { Authorization: `Bearer ${await grantFor(session)}` },
5716
5666
  }),
5717
- advertiseTools: true, // optional — named tools instead of meta-tools
5667
+ advertiseTools: true,
5718
5668
  });
5719
5669
  ```
5720
5670
 
5721
- The callback is evaluated on **every local turn**, including reminder
5722
- fires and post-restart follow-ups, so the identity always comes from the
5723
- session itself, never from state parked in memory. The model never sees a
5724
- tenant parameter and can never choose the tenant. A callback that throws
5725
- fails the turn: a turn never silently runs without the connection's
5726
- identity. Local runtime only; cloud turns are refused. `host.mcp` calls
5727
- from server tools keep the static headers only. Not combinable with
5728
- `oauth: true`; the host OAuth provider owns the Authorization header.
5671
+ The identity always comes from the session itself, never from a tenant
5672
+ parameter the model could invent. A callback that throws fails the
5673
+ turn; a cloud turn with `auth` is refused instead of running without
5674
+ that identity. `host.mcp` calls from server tools keep the static
5675
+ headers only. You can't combine `auth` with `oauth: true`.
5729
5676
 
5730
5677
  Derive the identity from durable session facts: `session.auth`,
5731
- `session.id`, or your channel's own session state. Do **not** key it off
5732
- `session.continuationKey`: the HTTP channel rotates the continuation key
5733
- after every accepted follow-up, so a tenant mapping keyed on it silently
5734
- breaks mid-conversation. (Channels that mint stable, parseable tokens by
5735
- design are the exception.)
5736
-
5737
- `auth` works attached or advertised. Advertised connections open
5738
- per-operation clients with the evaluated headers. Attached connections
5739
- ride the turn's SDK `mcpServers`, passed on **every send** rather than
5740
- pinned on the cached per-session agent handle, so a rotated credential is
5741
- live on the very next turn. A stateful stdio server cannot share a
5742
- process with an attached `auth` connection. Advertise the auth
5743
- connection instead.
5744
-
5745
- ## Advertise a connection's tools by name (`advertiseTools`) {#advertise-tools}
5746
-
5747
- Set `advertiseTools: true` when the model should call an MCP server's tools
5748
- by name. The Agent SDK preserves each tool's name, description, input and
5749
- output schemas, and MCP annotations.
5678
+ `session.id`, or your channel's own session state. Do not key it off
5679
+ `session.continuationKey`. The HTTP channel rotates that key after every
5680
+ accepted follow-up, so a tenant mapping keyed on it breaks
5681
+ mid-conversation.
5682
+
5683
+ `auth` works attached or advertised. Advertise the auth connection
5684
+ instead of attaching it next to a stateful stdio server.
5685
+
5686
+ ## Advertise tools {#advertise-tools}
5687
+
5688
+ Set `advertiseTools: true` when the model should call an MCP server's
5689
+ tools by name. The Agent SDK preserves each tool's description, input
5690
+ and output schemas, and MCP annotations. Exposed names normalize invalid
5691
+ characters and add numeric suffixes to avoid collisions.
5750
5692
 
5751
5693
  ```ts
5752
5694
  export default defineConnection({
@@ -5756,20 +5698,22 @@ export default defineConnection({
5756
5698
  });
5757
5699
  ```
5758
5700
 
5759
- A listing failure, invalid tool name, or name collision fails the turn.
5760
- Advertised tools follow the same runtime support as server tools. They cannot
5761
- be called through the direct tool API.
5701
+ A listing or authentication failure fails the turn by default. Set
5702
+ `optional: true` to omit an unavailable connection instead. Advertised
5703
+ tools follow the same runtime support as server tools, and [direct tool
5704
+ calls](/docs/reference/tools.md#call-a-tool-without-a-model-turn) use the same names.
5762
5705
 
5763
- In a dry-run session, MCP tools marked read-only run normally. Tools marked
5764
- as writes are stubbed. Tools without effect annotations are unavailable.
5706
+ In a dry-run session, MCP tools marked read-only run normally. Tools
5707
+ marked as writes are stubbed. Tools without effect annotations are
5708
+ unavailable.
5765
5709
 
5766
- ## Restrict which tools a connection serves
5710
+ ## Filter connection tools
5767
5711
 
5768
5712
  Use `tools` the same way you allowlist harness tools on the agent. When
5769
5713
  set, the connection serves only those names. Use `disallowedTools` to
5770
- drop names instead. The two combine as deny-wins, same as the Cursor
5771
- SDK. The model and `host.mcp` only see what remains. On a model-visible
5772
- connection, set `advertiseTools: true` so the raw server is not attached.
5714
+ drop names instead. The two combine as deny-wins. The model and
5715
+ `host.mcp` only see what remains. A model-visible filter requires
5716
+ `advertiseTools: true`.
5773
5717
 
5774
5718
  ```ts
5775
5719
  export default defineConnection({
@@ -5777,7 +5721,9 @@ export default defineConnection({
5777
5721
  advertiseTools: true,
5778
5722
  tools: ["search_skus", "get_stock"],
5779
5723
  });
5724
+ ```
5780
5725
 
5726
+ ```ts
5781
5727
  export default defineConnection({
5782
5728
  url: "https://mcp.example.com/inventory",
5783
5729
  advertiseTools: true,
@@ -5786,9 +5732,9 @@ export default defineConnection({
5786
5732
  ```
5787
5733
 
5788
5734
  Names are the server's `tools/list` names. Unknown names are omitted. A
5789
- filter that matches nothing on the server fails the turn. Combine with
5790
- `effects: "read"` to keep only the listed tools the server classifies as
5791
- reads.
5735
+ filter that matches nothing on the server fails the turn unless
5736
+ `optional: true`. Combine with `effects: "read"` to keep only the listed
5737
+ tools the server classifies as reads.
5792
5738
 
5793
5739
  A list of names is an allowlist. An object of handlers authors TypeScript
5794
5740
  tools. On `host-connections/`, a name list restricts `host.mcp` without
@@ -5806,12 +5752,8 @@ export default defineConnection({
5806
5752
  });
5807
5753
  ```
5808
5754
 
5809
- This suits small purpose-built servers, like a `units` converter
5810
- shipped next to the agent.
5811
-
5812
- To run TypeScript in the agent environment (including a repo-less cloud
5813
- VM), author the tools on the connection instead. The Agent SDK packages
5814
- them as stdio MCP. You write `execute`. The Agent SDK speaks the protocol.
5755
+ To run TypeScript in the agent environment, including a repo-less cloud
5756
+ VM, author the tools on the connection instead. You write `execute`:
5815
5757
 
5816
5758
  ```ts
5817
5759
  export default defineConnection({
@@ -5829,12 +5771,12 @@ export default defineConnection({
5829
5771
  ## Cursor account MCP connection
5830
5772
 
5831
5773
  `{ cursorAccount: true }` forwards the MCP connectors the signed-in
5832
- Cursor account already authorized (dashboard → MCP): Linear, Notion,
5833
- Slack, and the rest. You don't configure tokens. Every tool runs on the
5774
+ Cursor account already authorized (dashboard → MCP), such as Linear,
5775
+ Notion, and Slack. You don't configure tokens. Every tool runs on the
5834
5776
  Cursor backend with the account's stored OAuth credentials, so raw
5835
- tokens never reach the serve host, session workspaces, or traces.
5777
+ tokens never reach the serving host, session workspaces, or traces.
5836
5778
 
5837
- By default the agent gets **every** connected HTTP/SSE connector on the
5779
+ By default the agent gets every connected HTTP/SSE connector on the
5838
5780
  account. Pass `servers: "*"` (or `["*"]`) for the same all-connectors
5839
5781
  behavior in an explicit form. Pass a name list when you want a smaller
5840
5782
  set.
@@ -5845,12 +5787,9 @@ export default defineConnection({
5845
5787
  cursorAccount: true,
5846
5788
  advertiseTools: true,
5847
5789
  });
5848
- // same, spelled out:
5849
- export default defineConnection({
5850
- cursorAccount: true,
5851
- servers: "*",
5852
- advertiseTools: true,
5853
- });
5790
+ ```
5791
+
5792
+ ```ts
5854
5793
  // only Linear:
5855
5794
  export default defineConnection({
5856
5795
  cursorAccount: true,
@@ -5861,17 +5800,15 @@ export default defineConnection({
5861
5800
 
5862
5801
  Name the file `account.ts`. `cursor.ts` collides with the IDE `cursor`
5863
5802
  MCP namespace. `advertiseTools: true` puts connector tools on local
5864
- turns by name. Without it they sit behind harness meta-tools.
5803
+ turns by name.
5865
5804
 
5866
5805
  The host must be signed in (`agent-sdk login`, `CURSOR_API_KEY`, or
5867
- `CURSOR_SERVICE_ACCOUNT_KEY`).
5868
- `serve` fails fast at startup otherwise, and logs each connector's live
5869
- status (`connected`, `needsAuth`, `error`) as it starts.
5806
+ `CURSOR_SERVICE_ACCOUNT_KEY`). `serve` fails at startup otherwise.
5870
5807
 
5871
5808
  Filtered account connections work on managed cloud deployments. A
5872
- self-hosted cloud agent with a concrete `servers` list needs `--public-url`.
5873
- Serve fails instead of ignoring the filter. Use a `{ command }` connection
5874
- for stdio servers.
5809
+ self-hosted cloud agent with a concrete `servers` list needs
5810
+ `--public-url`. Serve fails instead of ignoring the filter. Use a
5811
+ `{ command }` connection for stdio servers.
5875
5812
 
5876
5813
  > [!CAUTION]
5877
5814
  > Whoever can talk to the agent can drive these connectors, because they
@@ -5881,11 +5818,11 @@ for stdio servers.
5881
5818
  > for example an SSO proxy or the hosted alias token). Prefer
5882
5819
  > `--bearer-token` on shared hosts.
5883
5820
 
5884
- ## Peer MCP connection
5821
+ ## Peer MCP connection {#peer-mcp-connection}
5885
5822
 
5886
5823
  `{ agent: "<slug>" }` addresses another agent mounted on the same serve
5887
- host. The model gets the peer's `ask` and `check` (and `call_tool`)
5888
- tools and can delegate work to it:
5824
+ host. The model gets the peer's `ask` and `check` tools, plus
5825
+ `call_tool` when the peer has server tools, and can delegate work to it:
5889
5826
 
5890
5827
  ```ts
5891
5828
  export default defineConnection({
@@ -5897,63 +5834,51 @@ export default defineConnection({
5897
5834
  Unknown slugs and self-references fail `serve` at startup. Walkthrough:
5898
5835
  [Peer agents](/docs/guides/agent-to-agent.md#delegate-a-question-to-a-specialist).
5899
5836
 
5900
- ## Every model-visible MCP connection is available in three places
5901
-
5902
- A file under `agent/mcp-connections/` serves three consumers. Host
5903
- connections skip the first one.
5904
-
5905
- 1. **Cursor agent:** Attached connections ride SDK `mcpServers` behind
5906
- harness MCP meta-tools. Set `advertiseTools: true` so local turns see
5907
- named tools.
5908
- 2. **Server tools:** Deterministic host code composes MCP calls
5909
- through `ctx.host.mcp`:
5910
-
5911
- ```ts
5912
- export default defineTool({
5913
- description: "Search Linear issues.",
5914
- inputSchema: z.object({ query: z.string() }),
5915
- async execute({ query }, ctx) {
5916
- return ctx.host.mcp.callTool("linear", "list_issues", { query });
5917
- },
5918
- });
5919
- ```
5920
-
5921
- 3. **Channel and schedule handlers:** Webhooks hit MCP servers with no
5922
- model turn at all, through `args.host.mcp`:
5923
-
5924
- ```ts
5925
- POST("/sync", {
5926
- bodySchema: z.object({}),
5927
- handler: async (_req, { host }) => {
5928
- const result = await host.mcp.callTool("linear", "list_issues", {});
5929
- return Response.json(result);
5930
- },
5931
- });
5932
- ```
5933
-
5934
- The host registry is small: `host.mcp.names()` lists MCP connection names,
5935
- and `listTools(name)` / `callTool(name, tool, args)` open the client
5936
- lazily on first use.
5837
+ ## Call MCP from host code
5937
5838
 
5938
- ## What's next
5839
+ A file under `agent/mcp-connections/` is available to the Cursor agent,
5840
+ to server tools through `ctx.host.mcp`, and to channel and schedule
5841
+ handlers through `args.host.mcp`. Host connections skip the model.
5842
+
5843
+ ```ts
5844
+ // agent/tools/search_linear.ts
5845
+ import { defineTool } from "@cursor/july/tools";
5846
+ import { z } from "zod";
5939
5847
 
5940
- Continue with these pages:
5848
+ export default defineTool({
5849
+ description: "Search Linear issues.",
5850
+ inputSchema: z.object({ query: z.string() }),
5851
+ async execute({ query }, ctx) {
5852
+ return ctx.host.mcp.callTool("linear", "list_issues", { query });
5853
+ },
5854
+ });
5855
+ ```
5856
+
5857
+ `host.mcp.names()` lists connection names.
5858
+ `listTools(name)` / `callTool(name, tool, args)` call into a named
5859
+ connection.
5860
+
5861
+ ## Related
5941
5862
 
5942
5863
  - [Host MCP OAuth](/docs/guides/mcp-oauth.md): `mcp oauth`, Connect, `--store`
5943
5864
  - [Tools](/docs/reference/tools.md): authored tools that wrap MCP connections
5944
5865
  - [Webhooks](/docs/guides/webhooks.md): calling MCP connections from handlers
5866
+ - [Peer agents](/docs/guides/agent-to-agent.md): when a specialist is its
5867
+ own agent
5945
5868
 
5946
5869
  ---
5947
5870
 
5948
5871
  Source: /docs/reference/evals.md
5949
5872
 
5950
- # Evals reference
5873
+ # Evals
5951
5874
 
5952
- This page is the complete authoring and runner contract for
5953
- `@cursor/july/evals`. Start with the [Evals guide](/docs/evals.md) for the
5954
- workflow and first regression case.
5875
+ Evals run fixed cases against an agent and record whether its turns,
5876
+ tools, events, and output meet a contract. Files under the project-root
5877
+ `evals/` directory define cases with `@cursor/july/evals`; the runner
5878
+ discovers their IDs, executes them, and reports every assertion. See the
5879
+ [Evals guide](/docs/evals.md) for the regression workflow.
5955
5880
 
5956
- ## Discovery and case IDs
5881
+ ## Eval discovery and case IDs
5957
5882
 
5958
5883
  Eval files live under the project-root `evals/` directory and end in
5959
5884
  `.eval.ts` or `.eval.js`. The path under that directory becomes the
@@ -5992,15 +5917,15 @@ Those cases are `prs/checkout` and `prs/search` when the file is
5992
5917
  `evals/prs.eval.ts`. Case IDs must be unique single path segments.
5993
5918
 
5994
5919
  A file can also export an array of `defineEval` calls. The runner names
5995
- them with zero-padded indexes such as `sql/0000`. Use named `cases` for
5996
- handwritten scenarios and arrays for loaded datasets.
5920
+ them with zero-padded indexes such as `sql/0000`. Named `cases` give
5921
+ handwritten scenarios stable IDs; arrays fit loaded datasets.
5997
5922
 
5998
5923
  `iterations` repeats one datapoint from 1 to 100 times. For
5999
5924
  `iterations: 3`, `weather/nyc` expands to `weather/nyc/1`,
6000
5925
  `weather/nyc/2`, and `weather/nyc/3`; selecting `weather/nyc` runs all
6001
5926
  three.
6002
5927
 
6003
- ## Configuration
5928
+ ## Eval configuration
6004
5929
 
6005
5930
  Every running suite needs `evals/evals.config.ts` with
6006
5931
  `maxConcurrency`:
@@ -6021,38 +5946,39 @@ export default defineEvalConfig({
6021
5946
  | `timeoutMs` | Per-case timeout; case/file, CLI, then config precedence |
6022
5947
  | `judge` | Default model for `t.judge` |
6023
5948
  | `reporters` | Objects notified as cases and runs complete |
6024
- | `maxPlaygroundRuns` | Number of server-side batches kept in playground history |
5949
+ | `maxPlaygroundRuns` | Number of server-side batches kept in playground history; defaults to 20 |
6025
5950
 
6026
5951
  A case can override `description`, `tags`, `timeoutMs`, `iterations`,
6027
5952
  `judge`, `reporters`, and `metadata`. Case metadata merges over
6028
5953
  file-level metadata; case reporters add to the file's reporters.
6029
5954
 
6030
- ## Drive turns with `t.send`
5955
+ ## Send turns in a case
6031
5956
 
6032
5957
  `await t.send(message, options?)` runs one turn and waits until it
6033
5958
  finishes, fails, or parks for approval. Several sends in one test share
6034
5959
  the session.
6035
5960
 
6036
5961
  The returned turn exposes its assistant `message`, `sessionId`,
6037
- `events`, ordered `toolCalls`, `ok`, and turn `index`. Assertions on the
6038
- turn inspect only that turn; assertions on `t` inspect the whole run.
6039
- Useful run values include `t.reply`, `t.events`, `t.turns`,
6040
- `t.sessionId`, and the timeout `t.signal`.
5962
+ `events`, ordered `toolCalls`, `ok`, and one-based `index`. Assertions
5963
+ on the turn inspect only that turn; assertions on `t` inspect the whole
5964
+ case. Run values include `t.reply`, `t.events`, `t.turns`,
5965
+ `t.sessionId`, `t.iteration`, `t.iterations`, and the timeout
5966
+ `t.signal`.
6041
5967
 
6042
5968
  These options apply on the first send because they shape the session:
6043
5969
 
6044
5970
  | Option | Contract |
6045
5971
  | --- | --- |
6046
5972
  | `workspaceFiles` | Relative path-to-content map seeded into the session workspace |
6047
- | `workspaceDir` | Absolute local harness working directory |
5973
+ | `workspaceDir` | Absolute working directory for a new local session |
6048
5974
  | `cloud` | Cloud session options merged over the agent's static cloud config |
6049
5975
 
6050
- Use `turn.expectOk()` when later test steps depend on that turn
6051
- succeeding.
5976
+ Use `turn.expectOk()` when later test steps depend on the turn
5977
+ succeeding. It throws when the turn failed.
6052
5978
 
6053
5979
  ## Trajectory assertions
6054
5980
 
6055
- Assertions record failures and let the test continue, so one case
5981
+ Assertions record failures without stopping the test, so one case
6056
5982
  reports every violated contract.
6057
5983
 
6058
5984
  | Assertion | Checks |
@@ -6175,53 +6101,43 @@ export default rows.map(row =>
6175
6101
  );
6176
6102
  ```
6177
6103
 
6178
- Materialize API-backed evidence before running a large suite. Commit
6179
- the exact payload, diff, or metadata revision and seed it with
6180
- `workspaceFiles`.
6181
-
6182
6104
  ## Reporters and results
6183
6105
 
6184
6106
  Built-in reporters include `JUnit({ filePath, suiteName? })` and
6185
6107
  `Artifacts({ dir })`. Custom reporters can expose `onRunStart`,
6186
- `onEvalComplete`, and `onRunComplete`. Reporter errors are logged and
6187
- do not change the eval verdict.
6108
+ `onEvalComplete`, and `onRunComplete`.
6188
6109
 
6189
- JSON output contains run totals plus one result per case. A result can
6190
- include the case ID, verdict, assertions, session ID, inputs, tool
6191
- calls, metrics, logs, duration, metadata, tags, final text, and error or
6192
- skip details.
6110
+ JSON output contains run totals and one result per case. Results include
6111
+ the case ID, verdict, assertions, session ID, inputs, final text, tools,
6112
+ tool calls, metrics, logs, duration, metadata, tags, and any error or
6113
+ skip reason.
6193
6114
 
6194
- ## CLI selection and artifacts
6115
+ ## Select cases
6195
6116
 
6196
6117
  ```bash
6197
6118
  agent-sdk eval --dir . --list
6198
6119
  agent-sdk eval --dir .
6199
6120
  agent-sdk eval --dir . builds/checkout
6200
6121
  agent-sdk eval --dir . --tag smoke
6201
- agent-sdk eval --dir . --verbose
6202
6122
  ```
6203
6123
 
6204
6124
  ID filters match an exact ID and its descendants. Repeated IDs use OR;
6205
6125
  repeated tags use OR. When both are present, a case must match both
6206
6126
  groups.
6207
6127
 
6208
- The local runner starts an ephemeral server. Default artifacts go under
6209
- `evals/<stamp>/` in the project state directory and include
6128
+ ## Inspect run artifacts
6129
+
6130
+ Default run artifacts go under `evals/<stamp>/` in the project state
6131
+ directory and include
6210
6132
  `summary.json`, `results.jsonl`, and `evals/<case-id>.json`.
6211
6133
  `--artifacts <dir>` chooses another destination; `--no-artifacts`
6212
6134
  disables them. This location is independent of `--state-root`.
6213
6135
 
6214
- Model and judge turns resolve credentials in this order:
6215
- `CURSOR_API_KEY`, `CURSOR_API_KEY_FILE`,
6216
- `CURSOR_SERVICE_ACCOUNT_KEY`, then `agent-sdk login`. Listing cases
6217
- needs no credential.
6218
-
6219
- ## Playground and hosted runs
6136
+ ## Run evals against a server
6220
6137
 
6221
- The playground **Evals** view starts batches on its running server and
6222
- keeps their sessions in the normal session list. `--url` targets a
6223
- named running server; `--prod --slug <slug>` targets a hosted
6224
- deployment.
6138
+ The playground **Evals** view starts batches on its running server.
6139
+ `--url` targets a named running server, and `--prod --slug <slug>`
6140
+ targets a hosted deployment.
6225
6141
 
6226
6142
  ```bash
6227
6143
  agent-sdk eval --prod --slug vulnerability-scanner --tag smoke
@@ -6229,17 +6145,16 @@ agent-sdk eval status <eval-id> --prod --slug vulnerability-scanner
6229
6145
  agent-sdk eval cancel <eval-id> --prod --slug vulnerability-scanner
6230
6146
  ```
6231
6147
 
6232
- Server-side batches use the target server's discovered evals and store
6233
- results in its playground history. Local-only reporter and concurrency
6234
- flags do not apply to `--url` or `--prod` runs.
6148
+ Server-side batches use the target's discovered cases and return an eval
6149
+ ID for `status` or `cancel`. Local reporter and concurrency flags don't
6150
+ apply to `--url` or `--prod` runs.
6235
6151
 
6236
6152
  ## Related
6237
6153
 
6238
- - [Evals guide](/docs/evals.md): author and run a regression workflow
6239
- - [CLI reference](/docs/reference/cli.md#eval): every runner flag
6240
- - [Sessions](/docs/reference/sessions.md): event vocabulary used by trajectory checks
6241
- - [Artifacts](/docs/reference/artifacts.md): durable outputs asserted by
6242
- `taggedArtifact`
6154
+ - [Evals guide](/docs/evals.md)
6155
+ - [CLI reference](/docs/reference/cli.md#eval)
6156
+ - [Sessions](/docs/reference/sessions.md)
6157
+ - [Artifacts](/docs/reference/artifacts.md)
6243
6158
 
6244
6159
  ---
6245
6160
 
@@ -6277,10 +6192,10 @@ The extension validates its configuration while the project loads.
6277
6192
  Missing or mistyped settings fail `agent-sdk validate` before a turn
6278
6193
  can call the contributed code.
6279
6194
 
6280
- Extension instructions are appended to the agent's prompt. The root
6281
- agent still needs its own `agent/instructions.md`.
6195
+ Extension instructions are appended to the root agent's instructions.
6196
+ The root agent still needs its own `agent/instructions.md`.
6282
6197
 
6283
- ## See what the extension added
6198
+ ## Inspect a mount
6284
6199
 
6285
6200
  Run discovery after mounting or upgrading an extension:
6286
6201
 
@@ -6368,18 +6283,14 @@ server. OAuth-backed plugin servers use
6368
6283
  [Host MCP OAuth](/docs/guides/mcp-oauth.md); transport and tool-filter
6369
6284
  details live in [MCP connections](/docs/reference/connections.md).
6370
6285
 
6371
- ## What an extension can contain
6372
-
6373
- Share capabilities that can safely join another agent under a
6374
- namespace:
6286
+ ## Extension contents
6375
6287
 
6376
- - Instructions, tools, skills, MCP and host connections
6377
- - Hooks, channels, schedules, and subagents
6378
- - Artifact kinds and local workspace seed files
6288
+ | Ships under the namespace | Ignored |
6289
+ | --- | --- |
6290
+ | Instructions, tools, skills, MCP and host connections | `agent.ts`, storage, OpenTelemetry |
6291
+ | Hooks, channels, schedules, and subagents | Playground code, custom sandbox backends |
6292
+ | Artifact kinds and workspace seed files | Nested extensions |
6379
6293
 
6380
- Agent-wide configuration does not compose through a mount.
6381
- `agent.ts`, storage, OpenTelemetry, playground code, custom sandbox
6382
- backends, and nested extensions are ignored in an extension package;
6383
6294
  `agent-sdk validate` reports unsupported paths.
6384
6295
 
6385
6296
  ## Build an extension
@@ -6446,18 +6357,16 @@ Source: /docs/reference/hooks.md
6446
6357
 
6447
6358
  # Hooks
6448
6359
 
6449
- A hook subscribes to the session event stream and runs a side effect
6450
- after each event is recorded: an audit line, a metric, a copy of the
6451
- transcript in your own store, or derived state for later turns. Hooks
6452
- run in the serving process for every session of the agent, on local and
6453
- cloud runtime turns alike.
6360
+ A hook subscribes to selected session events and can run a side effect
6361
+ after each matching event is recorded: an audit line, a metric, a
6362
+ transcript copy, or derived state. Hooks run for every session of the
6363
+ agent, on local and cloud turns alike. They cannot change the turn, the
6364
+ prompt, or the reply; a handler that throws is logged and skipped.
6454
6365
 
6455
- Hooks observe. They can't change the turn, the prompt, or the reply, and
6456
- a handler that throws is logged and skipped. Treat the event as
6457
- read-only; later subscribers see the same object. That makes hooks safe
6458
- to add to a production agent, and the wrong tool for anything that must
6459
- happen before the model runs or must fail a turn; see
6460
- [When not to use a hook](#when-not-to-use-a-hook).
6366
+ Treat the event as read-only. Later subscribers see the same object.
6367
+ That makes hooks safe to add to a production agent, and the wrong
6368
+ surface for anything that must run before the model or must fail a
6369
+ turn; see [Hook boundaries](#hook-boundaries).
6461
6370
 
6462
6371
  `defineHook` is unrelated to
6463
6372
  [Cursor Agent hooks](https://cursor.com/docs/agent/hooks), the
@@ -6506,95 +6415,54 @@ later sessions to read. Delete the file to opt out.
6506
6415
  ## Events and payloads
6507
6416
 
6508
6417
  Keys are event types from the
6509
- [event vocabulary](/docs/reference/sessions.md#which-events-can-i-stream), or `"*"`
6418
+ [event vocabulary](/docs/reference/sessions.md#stream-events), or `"*"`
6510
6419
  for every event. A typed key narrows `event.data`; a `"*"` handler
6511
6420
  receives the union, so switch on `event.type`. Every event carries the
6512
6421
  stream envelope `{ type, index, sessionId, turnId?, at, data }`, with
6513
- `turnId` set on turn-scoped events.
6514
-
6515
- The payloads hooks read most often:
6422
+ `turnId` set on turn-scoped events. Skip `*.appended` deltas when you
6423
+ want the final text; `message.completed` already has it.
6516
6424
 
6517
6425
  | Event | `event.data` |
6518
6426
  | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
6519
6427
  | `message.received` | `{ text }` |
6520
6428
  | `turn.completed` | `{ result?, usage?, cost? }`. `usage` has `inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`, and optional `reasoningTokens`. `cost` has `totalUsd` and the `model` it was priced against |
6521
- | `turn.failed` | `{ message }` |
6522
- | `actions.requested` | `{ calls: [{ callId, toolName, args? }] }`. A call with `parentCallId` belongs to a subagent |
6523
- | `action.result` | `{ callId, toolName, output?, isError, stubbed? }`. `stubbed` means a dry-run session answered a write without running it |
6429
+ | `turn.failed` | `{ message, status? }`. `status` is `"error"` or `"cancelled"` when present |
6430
+ | `actions.requested` | `{ calls: [{ callId, toolName, args?, parentCallId? }], parentCallId? }`. `parentCallId` marks subagent work |
6431
+ | `action.result` | `{ callId, toolName, output?, isError, stubbed?, parentCallId? }`. `stubbed` means a dry-run session answered a write without running it |
6524
6432
 
6525
6433
  The types are `SessionEvent`, `SessionEventType`, and `HookContext`,
6526
6434
  exported from `@cursor/july`.
6527
6435
 
6528
6436
  ## Handler context
6529
6437
 
6530
- | Member | What it is |
6531
- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
6532
- | `ctx.session` | Read-only session info: `id`, `channelId`, `mode` (`chat` or `task`), `purpose` (`live` or `eval`), `auth`, plus `title` and `sdkAgentId` when set |
6533
- | `ctx.agent` | `{ name }` of the agent the event belongs to |
6534
- | `ctx.channel` | `{ id, continuationToken }`. The token is `null` when the session can't take follow-ups |
6535
- | `ctx.host.kv` | Durable JSON, shared by every session of the agent; the storage backend decides whether it survives a hosted replace. Prefix keys with `ctx.session.id` for per-session state |
6536
- | `ctx.host.files` | Durable files, bound to this session. Pass `{ scope: "deployment" }` for agent-wide files |
6537
- | `ctx.host.otel` | Counters, histograms, and tags, attributed to this session |
6538
- | `ctx.host.mcp`, `ctx.host.github`, `ctx.host.slack` | The same shared clients tools get |
6539
- | `ctx.host.reminders` | Per-session [reminders](/docs/reference/schedules.md), the same API tools get |
6540
- | `ctx.artifacts` | Session-bound [artifacts](/docs/reference/artifacts.md) facade: `tag` fills in `sessionId` and `turnId` |
6541
- | `ctx.stateRoot` | Absolute path of the local state root. It resets when a hosted deployment is replaced; keep derived state in `kv` or `files` |
6542
-
6543
- ## When hooks run
6544
-
6545
- A hook runs after the event is durably recorded. It never delays the
6546
- model turn and never sees an event that wasn't recorded.
6547
-
6548
- Within one session, events dispatch in order, one at a time: the
6549
- channel's `events` handlers first, then each hook in discovery order.
6550
- Sessions don't wait on each other.
6551
-
6552
- Two consequences:
6553
-
6554
- - A slow handler holds up the next event's handlers for that session,
6555
- not the model. Keep handlers short and queue anything slow.
6556
- - Hooks fire for eval sessions too. Check
6557
- `ctx.session.purpose === "eval"` before metering or paging.
6558
-
6559
- Each event reaches a hook at most once. A restart doesn't replay the log
6560
- into hooks, so a mirror needs no dedupe, and the event log rather than
6561
- the hook's copy is the source of truth.
6562
-
6563
- ## Hooks, channel events, or evals?
6564
-
6565
- All of them consume the same stream, for different jobs:
6566
-
6567
- | | Hooks | Channel `events` | Evals |
6568
- | ------------------ | ----------------------------------------------- | ----------------------------------------------------------------- | --------------------------------- |
6569
- | Scope | every session of the agent | sessions the channel owns | one test turn |
6570
- | Job | observe: audit, metrics, mirrors, derived state | deliver: replies back to the channel's surface | assert: gates over the trajectory |
6571
- | Context | `ctx.host`, `ctx.artifacts`, session info | `channel.state`, `setContinuationToken`, `ctx.host`, session info | the `t` assertion helpers |
6572
- | Can affect the run | no | yes, it owns the surface | n/a |
6573
- | Authored at | `agent/hooks/*.ts` | channel config | `evals/**/*.eval.ts` |
6574
-
6575
- ## When not to use a hook
6576
-
6577
- | You want to | Use instead |
6578
- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
6579
- | Add context before the model runs | The channel's `send` message and `workspaceFiles`, `instructions.md`, skills, or `sandbox/workspace/` seed files |
6580
- | Reply on Slack, comment on a PR, or post any other delivery | The channel's `events` map, or the Slack and GitHub packs |
6581
- | Show PR progress (merge-box check, sticky banner) | `githubChannel({ progress: { commitStatus, banner } })`; see the [PR autofixer](/docs/templates/pr-autofixer.md) |
6582
- | Block, approve, or rewrite a tool call | [`needsApproval`](/docs/reference/tools.md#gate-a-tool-on-human-approval) on the tool |
6583
- | Act on the final assistant text, reject it for a same-turn repair, or fail a bad turn | `defineResult` |
6584
- | Gate a change on behavior | [Evals](/docs/evals.md) |
6585
-
6586
- ## Patterns
6587
-
6588
- Usage metering is the [authoring example](#author-a-hook). Three more:
6589
-
6590
- ### Alert on failure
6438
+ | Member | What it is |
6439
+ | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
6440
+ | `ctx.session` | Read-only session info: `id`, `channelId`, `mode` (`chat` or `task`), `purpose` (`live` or `eval`), `auth`, plus `title` and `sdkAgentId` when set |
6441
+ | `ctx.agent` | `{ name }` of the agent the event belongs to |
6442
+ | `ctx.channel` | `{ id, continuationToken }`. The token is `null` when the session can't take follow-ups |
6443
+ | `ctx.host.kv` | Durable JSON, shared by every session of the agent. Prefix keys with `ctx.session.id` for per-session state |
6444
+ | `ctx.host.files` | Durable files, bound to this session. Pass `{ scope: "deployment" }` for agent-wide files |
6445
+ | `ctx.host.otel` | Counters, histograms, and tags, attributed to this session |
6446
+ | `ctx.host.mcp`, `ctx.host.github`, `ctx.host.slack` | The same shared clients tools get |
6447
+ | `ctx.host.reminders` | Per-session [reminders](/docs/reference/schedules.md#reminders), the same API tools get |
6448
+ | `ctx.artifacts` | Session-bound [artifacts](/docs/reference/artifacts.md) facade: `tag` fills in `sessionId` and `turnId` |
6449
+ | `ctx.stateRoot` | Absolute path of the local state root. It resets when a hosted deployment is replaced; keep derived state in `kv` or `files` |
6450
+
6451
+ ## Hook dispatch
6452
+
6453
+ A hook runs after the event is recorded. It never delays the model and
6454
+ never sees an event that wasn't recorded.
6455
+
6456
+ | Rule | What happens |
6457
+ | --- | --- |
6458
+ | Same session | Events dispatch one at a time |
6459
+ | Eval sessions | The same stream fires; skip metering or paging when `ctx.session.purpose === "eval"` |
6460
+ | Host restart | Recorded events are not replayed into hooks, so a mirror needs no dedupe |
6461
+ | Slow handler | Holds the next event's handlers on that session, not the model. Keep handlers short and queue anything slow |
6591
6462
 
6592
- `turn.failed` carries the message, and `ctx.session.id` points at the
6593
- trace. Skip interrupted turns; those are preemptions, not failures. Read
6594
- secrets inside the handler: hosted deployments bind them after the
6595
- process starts, so a module-scope read stays empty. Give the call a
6596
- timeout, since a stalled request holds up later handlers on that
6597
- session.
6463
+ Read hosted secrets inside the handler, not at module scope. Give
6464
+ outbound calls a timeout; a stalled request holds later handlers on that
6465
+ session. Skip cancelled turns when paging; they record interrupted work.
6598
6466
 
6599
6467
  ```ts
6600
6468
  // agent/hooks/page-on-failure.ts
@@ -6607,7 +6475,7 @@ export default defineHook({
6607
6475
  if (
6608
6476
  pagerUrl === undefined ||
6609
6477
  ctx.session.purpose === "eval" ||
6610
- event.data.message === "turn interrupted"
6478
+ event.data.status === "cancelled"
6611
6479
  ) {
6612
6480
  return;
6613
6481
  }
@@ -6627,78 +6495,47 @@ export default defineHook({
6627
6495
  });
6628
6496
  ```
6629
6497
 
6630
- ### Mirror the transcript
6498
+ ## Hooks vs channels vs evals
6631
6499
 
6632
- Subscribe to `"*"` and write one file per event, skipping the
6633
- `*.appended` deltas: they arrive per token, and `message.completed`
6634
- carries the final text. Session scope keeps transcripts apart without a
6635
- session id in the path. The mirror holds reasoning text and raw tool
6636
- arguments and outputs, so pick the store accordingly, and write to your
6637
- own store instead when you need cross-session queries.
6500
+ All three consume the same stream, for different jobs:
6638
6501
 
6639
- ```ts
6640
- // agent/hooks/mirror.ts
6641
- import { defineHook } from "@cursor/july/hooks";
6642
-
6643
- export default defineHook({
6644
- events: {
6645
- async "*"(event, ctx) {
6646
- if (event.type.endsWith(".appended")) {
6647
- return;
6648
- }
6649
- const name = String(event.index).padStart(6, "0");
6650
- await ctx.host.files.write(
6651
- `transcript/${name}.json`,
6652
- JSON.stringify(event)
6653
- );
6654
- },
6655
- },
6656
- });
6657
- ```
6658
-
6659
- ### Keep derived state across a replace
6660
-
6661
- Write it to `ctx.host.kv` under a session-prefixed key; a tool reads it
6662
- back with `ctx.host.kv.get`.
6502
+ | | Hooks | Channel `events` | Evals |
6503
+ | ------------------ | ----------------------------------------------- | ----------------------------------------------------------------- | --------------------------------- |
6504
+ | Scope | every session of the agent | sessions the channel owns | one test turn |
6505
+ | Job | observe: audit, metrics, mirrors, derived state | deliver: replies back to the channel's surface | assert: gates over the trajectory |
6506
+ | Context | `ctx.host`, `ctx.artifacts`, session info | `channel.state`, `setContinuationToken`, `ctx.host`, session info | the `t` assertion helpers |
6507
+ | Can affect the run | no | yes, it owns the surface | n/a |
6508
+ | Authored at | `agent/hooks/*.ts` | channel config | `evals/**/*.eval.ts` |
6663
6509
 
6664
- ```ts
6665
- // agent/hooks/last-result.ts
6666
- import { defineHook } from "@cursor/july/hooks";
6510
+ ## Hook boundaries
6667
6511
 
6668
- export default defineHook({
6669
- events: {
6670
- async "turn.completed"(event, ctx) {
6671
- await ctx.host.kv.put(`last-result/${ctx.session.id}`, {
6672
- at: event.at,
6673
- result: event.data.result ?? null,
6674
- });
6675
- },
6676
- },
6677
- });
6678
- ```
6512
+ | You want to | Use instead |
6513
+ | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
6514
+ | Add context before the model runs | The channel's `send` message and `workspaceFiles`, `instructions.md`, skills, or `sandbox/workspace/` seed files |
6515
+ | Reply on Slack, comment on a PR, or post any other delivery | The channel's `events` map, or the Slack and GitHub packs |
6516
+ | Show PR progress (merge-box check, sticky banner) | `githubChannel({ progress: { commitStatus, banner } })`; see the [PR autofixer](/docs/templates/pr-autofixer.md) |
6517
+ | Block, approve, or rewrite a tool call | [`needsApproval`](/docs/reference/tools.md#gate-a-tool-on-human-approval) on the tool |
6518
+ | Act on the final assistant text, reject it for a same-turn repair, or fail a bad turn | `defineResult` |
6519
+ | Gate a change on behavior | [Evals](/docs/evals.md) |
6679
6520
 
6680
- ## Test and debug a hook
6521
+ ## Test a hook
6681
6522
 
6682
6523
  A hook definition is a plain object, so a unit test calls
6683
6524
  `hook.events["turn.completed"]` directly with an event and a stub
6684
6525
  `HookContext`. Discovery skips `*.test.ts`, so the test can live next to
6685
6526
  the hook.
6686
6527
 
6687
- At runtime:
6528
+ | Command | What it reports |
6529
+ | --- | --- |
6530
+ | `agent-sdk validate --dir .` | Discovery errors and the empty-handlers warning |
6531
+ | `agent-sdk info --dir . --json` | Loaded hooks under `agents[].hooks` |
6532
+ | `agent-sdk run --dir . --message "…"` | Serve log on stderr, including `hook "<name>" handler for <event> threw: …` |
6688
6533
 
6689
- - `agent-sdk validate --dir .` reports discovery errors and the
6690
- empty-handlers warning.
6691
- - `agent-sdk info --dir . --json` lists the loaded hooks under
6692
- `agents[].hooks`.
6693
- - Send a turn with `agent-sdk dev` or `agent-sdk run --dir . --message "…"`
6694
- and watch the serve log for
6695
- `hook "<name>" handler for <event> threw: …`. `run` prints that log on
6696
- stderr. On hosting, read it with [`agent-sdk logs`](/docs/reference/cli.md#logs).
6534
+ On hosting, read the same log with [`agent-sdk logs`](/docs/reference/cli.md#logs).
6697
6535
 
6698
- ## What's next
6699
-
6700
- Continue with these pages:
6536
+ ## Related
6701
6537
 
6538
+ - [Hooks guide](/docs/guides/hooks.md): meter usage and page on failure
6702
6539
  - [Sessions and streaming](/docs/reference/sessions.md): the event vocabulary hooks observe
6703
6540
  - [OpenTelemetry](/docs/guides/opentelemetry.md): OTLP traces and metrics
6704
6541
  from the same event stream
@@ -6710,34 +6547,33 @@ Continue with these pages:
6710
6547
 
6711
6548
  Source: /docs/reference/http-api.md
6712
6549
 
6713
- # HTTP API reference
6550
+ # HTTP API
6551
+
6552
+ Agent SDK hosts expose one public HTTP surface. In the default
6553
+ multi-agent layout, each agent uses `/<slug>/v1/*`; `--mode single`
6554
+ serves the same routes at `/v1/*`. Routes use the agent's HTTP auth
6555
+ chain, and session-owned resources return `403` to another principal.
6714
6556
 
6715
- Agent SDK hosts expose the same public HTTP surface. In the default
6716
- multi-agent layout each agent is namespaced under its slug
6717
- (`/<slug>/v1/session`, `/<slug>/playground`), with host-level routes at
6718
- the root. With `--mode single`, one agent serves the same surface
6719
- unslugged (`/v1/*`).
6557
+ Unless a section says otherwise, the default auth policy is
6558
+ `localDevStrict()`. `--bearer-token` replaces it with bearer auth, and
6559
+ `--allow-anonymous` replaces it with anonymous access. Built-in JSON
6560
+ routes use `{ ok: false, error: "<code>", message? }` for errors; MCP
6561
+ uses JSON-RPC, and custom channel handlers define their own responses.
6720
6562
 
6721
- Unless noted otherwise, routes run the agent's HTTP auth chain: the
6722
- default is `localDevStrict()` (loopback only), replaced by `bearerAuth` under
6723
- `--bearer-token` or `allowAll()` under `--allow-anonymous`. Session
6724
- routes also require the caller to be the session's owner (`403`
6725
- otherwise). Errors return JSON
6726
- `{ ok: false, error: "<code>", message? }` with a matching HTTP status.
6563
+ ## Host routes
6727
6564
 
6728
- ## Host-level routes (multi-agent mode)
6565
+ These routes live at the host root in multi-agent mode. The index routes
6566
+ exist only when the playground is enabled.
6729
6567
 
6730
- These routes live at the host root, above any agent. The two index
6731
- routes exist only while the playground is enabled (`--no-playground`
6732
- removes them) and run no auth. The documentation site is mounted in
6733
- both layouts and removed by `--no-docs`.
6568
+ | Route | Contract |
6569
+ | --- | --- |
6570
+ | `GET /` | HTML index of mounted agents; no auth |
6571
+ | `GET /v1/agents` | JSON index of mounted agents; no auth |
6572
+ | `GET /docs`, `GET /docs/*` | Documentation site in either layout; no auth |
6573
+ | `GET /v1/health` | Host liveness; no auth |
6734
6574
 
6735
- | Route | What it does |
6736
- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
6737
- | `GET /` | A web index of every mounted agent, linking to playgrounds (playground only) |
6738
- | `GET /v1/agents` | The JSON index of mounted agents (playground only, no auth) |
6739
- | `GET /docs`, `GET /docs/*` | This documentation, served as a static site (both layouts, no auth) |
6740
- | `GET /v1/health` | Host-level liveness, no auth |
6575
+ `--no-playground` removes the two index routes. `--no-docs` removes the
6576
+ documentation site.
6741
6577
 
6742
6578
  ## Start a session
6743
6579
 
@@ -6751,18 +6587,18 @@ curl -X POST http://127.0.0.1:3000/<slug>/v1/session \
6751
6587
  # "playgroundUrl":"…?sessionId=ses_…","traceUrl":"…/v1/session/ses_…/events"}
6752
6588
  ```
6753
6589
 
6754
- The response returns as soon as the message is accepted; follow the
6755
- stream for progress. The continuation token is the follow-up credential,
6756
- and `playgroundUrl` deep-links the session in the playground.
6590
+ Once accepted, the response returns `sessionId` for inspection and
6591
+ `continuationToken` for follow-ups; follow the stream for progress.
6757
6592
 
6758
- | Body field | Meaning |
6593
+ | Body field | Contract |
6759
6594
  | --- | --- |
6760
6595
  | `message` | Required user message |
6761
- | `title` | Session title |
6596
+ | `title` | Display title |
6762
6597
  | `dryRun` | Run read tools and stub write tools |
6763
- | `asOf` | ISO-8601 instant with a timezone, frozen at create; the prompt states it, `ctx.now()` returns it, and tool calls with relative, later-than-`asOf`, or omitted schema-declared time bounds are refused. `400` when unusable |
6764
- | `workspaceFiles` | UTF-8 files written into the session workspace |
6598
+ | `asOf` | ISO-8601 instant with a timezone; sets `ctx.now()` and rejects omitted, relative, or later declared tool time arguments. Invalid values return `400` |
6599
+ | `workspaceFiles` | Relative files added to the session workspace |
6765
6600
  | `cloud` | Per-session cloud options merged over the agent defaults |
6601
+ | `purpose` | Use `"eval"` to mark regression traffic |
6766
6602
 
6767
6603
  ## Send a follow-up
6768
6604
 
@@ -6774,15 +6610,15 @@ curl -X POST http://127.0.0.1:3000/<slug>/v1/session/ses_… \
6774
6610
  -d '{"continuationToken":"http:…","message":"Make it shorter."}'
6775
6611
  ```
6776
6612
 
6777
- Works for any chat session, including ones created by custom channels.
6778
- Each accepted follow-up rotates the token, and the response carries the
6779
- new one. Sending to a busy session interrupts the in-flight turn, waits
6780
- for it to settle, then sends.
6613
+ The route accepts any chat session, including one created by a custom
6614
+ channel. Each accepted follow-up rotates the continuation token and
6615
+ returns the replacement. A message sent to a busy session interrupts
6616
+ the active turn before starting.
6781
6617
 
6782
- Expect `409` on a stale token or a task session. Task sessions do not accept
6783
- follow-ups. Expect `403` when the caller is not the session owner.
6618
+ The route returns `409` for a stale token or task session, and `403`
6619
+ when the caller doesn't own the session.
6784
6620
 
6785
- ## Stream a session
6621
+ ## Stream or replay session events
6786
6622
 
6787
6623
  `GET /v1/session/:sessionId/stream` is the live NDJSON feed.
6788
6624
 
@@ -6790,44 +6626,36 @@ follow-ups. Expect `403` when the caller is not the session owner.
6790
6626
  curl -N 'http://127.0.0.1:3000/<slug>/v1/session/ses_…/stream?startIndex=0'
6791
6627
  ```
6792
6628
 
6793
- One NDJSON event per line, from `startIndex`, then following live. The
6794
- default is `0`: omitting the parameter replays the entire recorded
6795
- stream before following. Pass the last index you've seen plus one to
6796
- resume without duplicates. The stream is durable and reconnectable. For
6797
- the vocabulary, see
6798
- [Sessions](/docs/reference/sessions.md#which-events-can-i-stream).
6629
+ The route replays one event per line from `startIndex`, then follows new
6630
+ events. The default `0` replays the full stream. To reconnect without
6631
+ duplicates, pass the last index you received plus one.
6799
6632
 
6800
6633
  `GET /v1/session/:sessionId/events` returns a one-shot NDJSON dump.
6801
- Pass `?format=json` for `{ sessionId, events, playgroundUrl }`.
6802
-
6803
- ## Stop and list
6634
+ Pass `?format=json` for `{ sessionId, events, playgroundUrl }`. See
6635
+ [Stream events](/docs/reference/sessions.md#stream-events) for the event vocabulary.
6804
6636
 
6805
- `POST /v1/session/:sessionId/stop` interrupts the in-flight turn without
6806
- sending a new message. `GET /v1/sessions` lists sessions owned by the
6807
- calling principal. Under `serve --dev` on loopback it includes all
6808
- sessions, which is how webhook and schedule sessions show up in the
6809
- playground.
6637
+ ## Manage sessions
6810
6638
 
6811
- ## Session cost
6812
-
6813
- `GET /v1/session/:sessionId/cost` returns the session's cost report:
6814
- per-turn token usage and the engine's estimated cost, folded from
6815
- `turn.completed` events. It runs the same owner check as the other
6816
- session routes and returns `404` for an unknown session. The
6817
- [`agent-sdk cost`](/docs/reference/cli.md#cost) command reports the same data.
6639
+ | Route | Contract |
6640
+ | --- | --- |
6641
+ | `POST /v1/session/:sessionId/stop` | Interrupt the active turn without sending another message |
6642
+ | `GET /v1/sessions` | List sessions owned by the caller |
6643
+ | `GET /v1/session/:sessionId/cost` | Return per-turn token usage and estimated cost |
6818
6644
 
6819
- ## Approvals
6645
+ On loopback under `serve --dev`, the session list includes every
6646
+ principal so webhook and schedule sessions appear in the playground.
6647
+ The cost route returns `404` for an unknown session.
6820
6648
 
6821
- Two routes list and resolve parked tool calls.
6649
+ ## Resolve tool approvals
6822
6650
 
6823
- | Route | What it does |
6824
- | ----------------------------------------------- | -------------------------------------------------------------- |
6825
- | `GET /v1/session/:sessionId/approvals` | Pending human-in-the-loop tool approvals |
6826
- | `POST /v1/session/:sessionId/approvals/:callId` | Resolve one: `{"decision":"approve"}` or `{"decision":"deny"}` |
6651
+ | Route | Contract |
6652
+ | --- | --- |
6653
+ | `GET /v1/session/:sessionId/approvals` | List pending tool approvals |
6654
+ | `POST /v1/session/:sessionId/approvals/:callId` | Resolve one with `{"decision":"approve"}` or `{"decision":"deny"}` |
6827
6655
 
6828
6656
  For the lifecycle, see [Gate a tool on human approval](/docs/reference/tools.md#gate-a-tool-on-human-approval).
6829
6657
 
6830
- ## Call a tool directly
6658
+ ## Call a tool without a model turn
6831
6659
 
6832
6660
  `POST /v1/tools/:toolName` runs a server tool with no model turn.
6833
6661
 
@@ -6839,133 +6667,118 @@ curl -X POST http://127.0.0.1:3000/<slug>/v1/tools/inspect_pr \
6839
6667
  # "isError":false,"result":{…},"durationMs":12}
6840
6668
  ```
6841
6669
 
6842
- It runs an authored server tool in-process: schema-validated, no model
6843
- turn. An optional `"sessionId"` in the body runs it inside an existing
6844
- session and records it on that session's stream (`409 session_busy`
6845
- for a write-effect call while a turn runs; reads run alongside the
6846
- turn). Agent-execution tools are rejected with `400`, and
6847
- unknown tools with `404` and the list of available names. For the
6848
- semantics, see [Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn).
6670
+ | Body field | Contract |
6671
+ | --- | --- |
6672
+ | `input` | Tool input; defaults to `{}` |
6673
+ | `sessionId` | Bind the call to an existing session |
6674
+ | `continuationToken` | Bind the call by its wire continuation token; mutually exclusive with `sessionId` |
6849
6675
 
6850
- An optional `"continuationToken"` (`<channelId>:<key>`, as
6851
- `/v1/sessions` lists it; mutually exclusive with `sessionId`) addresses
6852
- the session by continuation token instead; malformed tokens are
6853
- rejected with `400 invalid_continuation_token`. For the semantics, see
6854
- [Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn).
6676
+ Omit both identifiers for an unbound call. Agent-execution tools return
6677
+ `400`, and an unknown name returns `404` with the available names. See
6678
+ [Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn) for session
6679
+ binding, busy-session rules, and error codes.
6855
6680
 
6856
- ## Discovery
6681
+ ## Discovery routes
6857
6682
 
6858
6683
  These read-only routes describe the running agent.
6859
6684
 
6860
- | Route | What it does |
6861
- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
6862
- | `GET /v1/info` | The discovered surface: model, tools, skills, MCP connections, subagents, channels and routes (with schemas), schedules, hooks, diagnostics |
6863
- | `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` |
6864
- | `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 |
6865
- | `GET /v1/health` | Per-agent liveness, no auth |
6866
- | `GET /v1/logs?after=N` | Recent server log lines, with a polling cursor |
6685
+ | Route | Contract |
6686
+ | --- | --- |
6687
+ | `GET /v1/info` | Return the discovered model, tools, skills, connections, subagents, channels, schedules, hooks, and diagnostics |
6688
+ | `GET /v1/tools` | Return live server tools and advertised MCP tools as `{ name, title?, source? }`; bind tenant-scoped listings with `session` or `continuationToken` |
6689
+ | `GET /v1/tools/:name` | Return one tool's description, execution mode, approval and effect rules, schemas, and source |
6690
+ | `GET /v1/health` | Return per-agent liveness; no auth |
6691
+ | `GET /v1/logs?after=N` | Return recent server logs and the next polling cursor |
6867
6692
 
6868
- ## Artifacts
6693
+ An unknown tool name returns `404` with available names. If an MCP
6694
+ connection can't list its tools, the catalog skips that connection and
6695
+ includes it in `connectionErrors`.
6869
6696
 
6870
- Two routes read durable artifacts tagged by `ctx.artifacts` or
6871
- `tag_artifact`. See [Artifacts](/docs/reference/artifacts.md).
6697
+ ## List and download artifacts
6872
6698
 
6873
- | Route | What it does |
6874
- | -------------------------------- | ------------------------------------------------------------------------------------------------------ |
6875
- | `GET /v1/artifacts` | List artifacts as `{ artifacts }`, newest-updated first. Filter with `?kind=`, `?sessionId=`, and `?limit=` (a positive integer) |
6876
- | `GET /v1/artifacts/:id/content` | Download one artifact's file or blob payload. Served as an attachment, never rendered inline; `404` when the artifact is unknown or carries no content |
6699
+ | Route | Contract |
6700
+ | --- | --- |
6701
+ | `GET /v1/artifacts` | Return `{ artifacts }`, newest-updated first; filter with `kind`, `sessionId`, and a positive `limit` |
6702
+ | `GET /v1/artifacts/:id/content` | Download an artifact's file or blob as an attachment; return `404` when no content exists |
6877
6703
 
6878
- Session ownership applies the same way as `GET /v1/sessions`: under
6879
- `serve --dev` on loopback (or `--allow-anonymous`) the list spans all
6880
- principals, while bearer or custom channel auth keeps strict
6881
- per-principal isolation.
6704
+ The list follows the same ownership rules as `GET /v1/sessions`. See
6705
+ [Artifacts](/docs/reference/artifacts.md) for tagging and record fields.
6882
6706
 
6883
- ## Custom channel routes
6707
+ ## Call custom channel routes
6884
6708
 
6885
- Authored routes mount under `/v1/channels/<id>` with the methods, paths,
6886
- and Zod schemas the channel declared (a
6887
- `POST /<slug>/v1/channels/drive` route, say). Bodies are validated before
6888
- handlers run (`400` on schema violations), and each channel's auth chain
6889
- applies. The GitHub channel verifies `X-Hub-Signature-256` when a
6890
- secret is configured. See [Channels](/docs/reference/channels.md).
6709
+ Authored routes mount under `/v1/channels/<id>` with their declared
6710
+ methods and paths. The host validates their Zod body and query schemas
6711
+ before calling the handler, returning `400` on failure. Each channel's
6712
+ auth chain applies. See [Channels](/docs/reference/channels.md).
6891
6713
 
6892
6714
  ## MCP endpoint
6893
6715
 
6894
- `/v1/mcp` serves the Model Context Protocol over streamable HTTP
6895
- (stateless; POST carries the protocol, and GET/DELETE return
6896
- spec-compliant 405s). The tools are `ask` (delegate a message, bounded
6897
- waits), `check` (poll a running session), and `call_tool` (deterministic
6898
- server-tool passthrough, present when the agent has server tools). The
6899
- route runs the same auth chain as the session API. Peer wiring:
6900
- [MCP connections](/docs/reference/connections.md#peer-mcp-connection).
6716
+ Both MCP routes use stateless streamable HTTP. Send protocol requests
6717
+ with `POST`; `GET` and `DELETE` return `405`.
6901
6718
 
6902
- `/v1/mcp/tools` is a second stateless MCP endpoint exposing only the
6903
- agent's deterministic server tools. Hosted cloud turns call back into
6904
- it through the URL configured by `serve --cloud-tools-url`. Unlike
6905
- `/v1/mcp`, it runs the CLI-level auth chain (loopback, bearer, or
6906
- anonymous), not any authored channel auth.
6719
+ | Route | Tools and auth |
6720
+ | --- | --- |
6721
+ | `/v1/mcp` | `ask`, `check`, and `call_tool` when server tools exist; uses the session API auth chain |
6722
+ | `/v1/mcp/tools` | Deterministic server tools only; uses the CLI-level loopback, bearer, or anonymous auth chain |
6723
+
6724
+ See [MCP connections](/docs/reference/connections.md#peer-mcp-connection) to connect
6725
+ one agent to another.
6907
6726
 
6908
6727
  ## Playground eval routes
6909
6728
 
6910
- The playground Evals tab and `agent-sdk eval --prod` / `--url` use these:
6729
+ The playground Evals tab and remote eval commands use these routes:
6911
6730
 
6912
- | Route | What it does |
6913
- | ------------------------------- | ---------------------------------------------------------------------------------------------------------- |
6914
- | `GET /v1/dev/evals` | List discovered eval datapoints and project config |
6915
- | `GET /v1/dev/evals/runs` | List recent run snapshots, newest first |
6916
- | `POST /v1/dev/evals/runs` | Start an eval run (`{filterIds?, tags?}`); `202` with a snapshot (`runId` is the Eval ID), `404` when nothing matches, `409` when one is running |
6917
- | `GET /v1/dev/evals/runs/:runId` | Poll a run's progress |
6918
- | `POST /v1/dev/evals/runs/:runId/cancel` | Cancel a running batch; `200` with snapshot, `404` unknown, `409` when not running |
6731
+ | Route | Contract |
6732
+ | --- | --- |
6733
+ | `GET /v1/dev/evals` | List discovered cases and eval config |
6734
+ | `GET /v1/dev/evals/runs` | List recent run snapshots, newest first |
6735
+ | `POST /v1/dev/evals/runs` | Start `{ filterIds?, tags?, timeoutMs?, verbose? }`; return `202`, `404` for no match, or `409` while another run is active |
6736
+ | `GET /v1/dev/evals/runs/:runId` | Return progress and the final snapshot |
6737
+ | `POST /v1/dev/evals/runs/:runId/cancel` | Cancel an active run; return `404` when unknown or `409` when no longer running |
6919
6738
 
6920
- Eval runs are asynchronous. Poll the run route for case progress and
6921
- the final `completed` or `failed` status. Batch errors appear on the
6922
- snapshot returned by the poll. Entries within `filterIds` and `tags`
6923
- use OR semantics. When both fields are present, a case must match one
6924
- entry from each field. Listed runs persist across restarts when
6925
- durable storage is configured. Otherwise they are
6926
- process-memory only.
6739
+ Poll the run route until its status is `completed`, `failed`, or
6740
+ `cancelled`. Entries within `filterIds` and `tags` use OR semantics;
6741
+ when both fields are present, a case must match each group.
6927
6742
 
6928
6743
  ## Dev-mode routes
6929
6744
 
6930
6745
  These routes exist only under `serve --dev`.
6931
6746
 
6932
- | Route | What it does |
6933
- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
6747
+ | Route | Contract |
6748
+ | --- | --- |
6934
6749
  | `POST /v1/dev/schedules/:scheduleId` | Dispatch a schedule by hand, exactly once. Returns `{scheduleId, sessionIds}` |
6935
- | `GET /v1/dev/reminders` | List reminders |
6936
- | `POST /v1/dev/reminders/:reminderId` | Fire a reminder by hand |
6750
+ | `GET /v1/dev/reminders` | List reminders |
6751
+ | `POST /v1/dev/reminders/:reminderId` | Fire a reminder by hand |
6937
6752
 
6938
6753
  Schedules and reminders never fire automatically in dev mode. These
6939
- routes are the only way they run, which keeps iteration deterministic.
6754
+ routes run them manually.
6940
6755
 
6941
6756
  ## Playground assets
6942
6757
 
6943
6758
  `GET /playground` and `GET /playground/assets/:file` serve the
6944
- playground (omitted with `--no-playground`). It calls the JSON API
6945
- above and has no privileged surface.
6759
+ playground. `--no-playground` removes both routes.
6946
6760
 
6947
6761
  ## Status codes
6948
6762
 
6949
- Error responses use a small, consistent set of status codes.
6950
-
6951
- | Code | Meaning here |
6952
- | ----- | -------------------------------------------------------------------------------------------------------------------------- |
6953
- | `400` | Schema-invalid body or query, agent-execution tool called on the host, malformed request |
6954
- | `401` | No auth policy admitted the request |
6955
- | `403` | Authenticated, but not the session owner |
6956
- | `404` | Unknown session, tool, schedule, reminder, or eval run; no eval datapoints match a run request |
6957
- | `405` | Wrong method (GET on the MCP endpoint, say) |
6958
- | `409` | Stale continuation token, a busy session-bound tool call, a non-followable task session, or an eval run already in progress |
6959
- | `202` | Accepted for background work (GitHub `{ task }` hooks, eval runs) |
6763
+ Built-in routes use these common status codes.
6960
6764
 
6961
- ## What's next
6765
+ | Code | Meaning |
6766
+ | --- | --- |
6767
+ | `400` | Invalid body, query, tool input, or request shape |
6768
+ | `401` | No auth policy admitted the request |
6769
+ | `403` | The caller is authenticated but doesn't own the resource |
6770
+ | `404` | A named resource doesn't exist, or an eval selection matches no cases |
6771
+ | `405` | The route doesn't accept this method |
6772
+ | `409` | The resource state rejects the request, such as a stale token or busy write |
6773
+ | `202` | The request was accepted for asynchronous work |
6774
+ | `500` | A write-effect tool can't create its session workspace (`workspace_unavailable`) |
6962
6775
 
6963
- Continue with these pages:
6776
+ ## Related
6964
6777
 
6965
- - [Sessions and streaming](/docs/reference/sessions.md): the handles and events these
6966
- routes traffic in
6967
- - [Channels](/docs/reference/channels.md): authoring your own routes
6968
- - [Deployment](/docs/deployment.md): auth on real hosts
6778
+ - [Sessions](/docs/reference/sessions.md)
6779
+ - [Channels](/docs/reference/channels.md)
6780
+ - [Tools](/docs/reference/tools.md)
6781
+ - [Deployment](/docs/deployment.md)
6969
6782
 
6970
6783
  ---
6971
6784
 
@@ -6973,19 +6786,17 @@ Source: /docs/reference/instructions.md
6973
6786
 
6974
6787
  # Instructions
6975
6788
 
6976
- `agent/instructions.md` is the always-on system prompt. It's the one
6977
- piece of prose the model sees on every turn. It's required on the root
6978
- agent; subagents may inline `instructions` in their `agent.ts` instead.
6789
+ Agent instructions form the always-on system prompt and reach the model
6790
+ on every turn. A root agent requires them; a subagent may inline
6791
+ `instructions` in `agent.ts` instead.
6979
6792
 
6980
6793
  ## Authoring forms
6981
6794
 
6982
- Three forms cover every case.
6983
-
6984
- | Form | Reach for it when |
6795
+ | Form | Use it when |
6985
6796
  | --- | --- |
6986
- | `agent/instructions.md` | Plain Markdown for most agents. |
6987
- | `agent/instructions.ts` | Generated prompts. Default-export `defineInstructions({ markdown })` or a plain string. |
6988
- | `agent/instructions/` directory | A long prompt split across files, composed in filename order. |
6797
+ | `agent/instructions.md` | Plain Markdown for most agents |
6798
+ | `agent/instructions.ts` | Generated prompts. Default-export `defineInstructions({ markdown })` or a plain string |
6799
+ | `agent/instructions/` directory | A long prompt split across files, composed in filename order |
6989
6800
 
6990
6801
  ```ts
6991
6802
  // agent/instructions.ts
@@ -6996,22 +6807,17 @@ export default defineInstructions({
6996
6807
  });
6997
6808
  ```
6998
6809
 
6999
- ## How instructions reach the model
7000
-
7001
- On the local runtime, instructions land in the session workspace as
7002
- `AGENTS.md`, and the harness loads them natively. On the cloud runtime,
7003
- they're prepended to the first prompt, because the cloud VM doesn't
7004
- share the local session workspace.
6810
+ ## Delivery
7005
6811
 
7006
- The local workspace is a real Cursor project directory, so the harness
7007
- may also load ambient `AGENTS.md` and `.cursor` config from ancestor
7008
- directories. [Agent config Local cwd](/docs/reference/agent-config.md#local-cwd)
7009
- covers controlling that.
6812
+ Local and cloud turns receive the composed instructions. They aren't
6813
+ written into the session workspace. Parent directories can still
6814
+ contribute ambient `AGENTS.md` and `.cursor` settings; [Agent config:
6815
+ local cwd](/docs/reference/agent-config.md#local-cwd) covers how to control that.
7010
6816
 
7011
- ## What to put in instructions
6817
+ ## Contents
7012
6818
 
7013
- Keep them a few lines: identity, when to use which tool, output shape.
7014
- The [quickstart PR approver](/docs/quickstart.md) is the pattern:
6819
+ Keep them a few lines: identity, when to use which tool, and the output
6820
+ shape. The [quickstart PR approver](/docs/quickstart.md) is the pattern:
7015
6821
 
7016
6822
  ```md
7017
6823
  # PR approver
@@ -7025,21 +6831,13 @@ You review GitHub pull requests. Be specific and brief.
7025
6831
  End with one sentence: the verdict and why.
7026
6832
  ```
7027
6833
 
7028
- - Name the tools and the decision rule ("use X before answering about
7029
- Y"), not general encouragement.
7030
- - State the output contract: length, format, fences. That contract is
7031
- what your [evals](/docs/evals.md) gate.
7032
- - Move procedures to [skills](/docs/reference/skills.md). A multi-step workflow the
7033
- model only sometimes needs belongs in `agent/skills/`, where it loads
7034
- on demand and keeps the always-on prompt small.
7035
-
7036
- Instructions are the third lever in the
7037
- [hillclimbing loop](/docs/hillclimbing.md), after host preparation and evidence
7038
- shape. If a fixture keeps failing, look there before rewriting prose.
6834
+ Name the tools and the decision rule ("use X before answering about
6835
+ Y"), not general encouragement. State the output contract, including
6836
+ length, format, and fences, so your [evals](/docs/evals.md) can gate it.
6837
+ Put multi-step workflows the model only sometimes needs in
6838
+ `agent/skills/`; they load on demand and keep the always-on prompt small.
7039
6839
 
7040
- ## What's next
7041
-
7042
- Continue with these pages:
6840
+ ## Related
7043
6841
 
7044
6842
  - [Skills](/docs/reference/skills.md): procedures the model loads only when relevant
7045
6843
  - [Agent config](/docs/reference/agent-config.md): the file next to this one
@@ -7052,55 +6850,38 @@ Source: /docs/reference/playground.md
7052
6850
 
7053
6851
  # Playground
7054
6852
 
7055
- Every served agent ships with a web playground at
6853
+ `agent-sdk serve` enables a web playground at
7056
6854
  `http://127.0.0.1:3000/<slug>/playground` (or `/playground` in single
7057
- mode). Anything you can do there you can also do with curl.
7058
-
7059
- ## What it does
7060
-
7061
- Use the playground to chat, try channel routes, and inspect sessions.
7062
-
7063
- - **Chat** with the agent. Text and reasoning stream live, and tool
7064
- calls appear inline with their arguments, output, and error state.
7065
- - **Slash commands**: custom channel routes become composer commands
7066
- (a `drive` route becomes `/drive <pr-url>`), with `/help` and
7067
- autocomplete.
7068
- - **Try** any channel route from the Agent surface. The modal remembers
7069
- your last body per endpoint and has Copy curl, and a successful Try
7070
- opens the created session.
7071
- - **Sessions**: browse the sessions you own (chat, custom-channel,
7072
- schedule tasks) and replay their event streams. In `--dev` on
7073
- loopback, or with `--allow-anonymous`, the list includes every
7074
- principal. Search by session ID to filter the list, or press Enter
7075
- to open an ID directly. "Open trace" renders a saved event stream.
7076
- - **Approvals**: parked `needsApproval` tool calls render Approve /
7077
- Deny buttons.
7078
- - **Evals**: list and run filesystem evals from the browser (backed by
7079
- `/v1/dev/evals`). Schedule hand-dispatch still requires `--dev`.
7080
- - **The surface**: inspect the discovered tools, skills, subagents, MCP
7081
- connections, channels, and hooks.
7082
- - **Custom tool chips**: drop `agent/playground/tools/<toolName>.tsx` to
7083
- change how that tool renders. Chips compile from the agent tree;
7084
- an [extension](/docs/reference/extensions.md) cannot contribute them.
7085
- - **Raw events pane**: flip it on to inspect the event stream.
7086
- - **Logs tab**: recent server log lines, polled from `GET /v1/logs`.
7087
-
7088
- In multi-agent mode each agent has its own playground at
7089
- `/<slug>/playground`, and `/` is an index of them all.
7090
-
7091
- ## Share it beyond localhost
6855
+ mode). In multi-agent mode, each agent has its own playground, and `/`
6856
+ lists them all.
6857
+
6858
+ ## Playground surfaces
6859
+
6860
+ | Surface | What you can do |
6861
+ | --- | --- |
6862
+ | Chat | Talk to the agent. Text and reasoning stream live, and tool calls appear inline with their arguments, output, and error state |
6863
+ | Slash commands | Custom channel routes without path parameters become composer commands; GitHub and Slack ingress routes are excluded. A `drive` route becomes `/drive <pr-url>`, with `/help` and autocomplete |
6864
+ | Try | Invoke a custom channel route from the Agent tab. The modal remembers your last body per endpoint, copies curl, and opens a session when the route creates one |
6865
+ | Runs | Browse the sessions you own and eval runs. Search by title or identifier; open sessions in Chat, Trace, or Raw and eval runs in Evals |
6866
+ | Approvals | Parked `needsApproval` tool calls render Approve / Deny buttons |
6867
+ | Evals | List and run filesystem evals from the browser |
6868
+ | Agent | Inspect the discovered tools, skills, subagents, MCP connections, channels, and hooks |
6869
+ | Raw | Inspect the selected session's event stream |
6870
+ | Logs | Recent server log lines, polled from `GET /v1/logs` |
6871
+
6872
+ In `--dev` on loopback, or with `--allow-anonymous`, the session list
6873
+ includes every principal.
6874
+
6875
+ ## Remote access
7092
6876
 
7093
6877
  The default `localDevStrict()` auth admits direct loopback calls only
7094
6878
  and rejects proxy-forwarding headers, so a tunnel or LAN address won't
7095
- work
7096
- until you pass `--bearer-token <secret>` (or
6879
+ work until you pass `--bearer-token <secret>` (or
7097
6880
  `serve(dir, { authToken })`). Open the playground on the remote device
7098
- and paste the token into the token field in the navbar. `--allow-anonymous` is the
7099
- demo-only alternative for trusted networks.
6881
+ and paste the token into the token field in the navbar.
6882
+ `--allow-anonymous` is the demo-only alternative for trusted networks.
7100
6883
 
7101
- ## What's next
7102
-
7103
- Continue with these pages:
6884
+ ## Related
7104
6885
 
7105
6886
  - [HTTP API](/docs/reference/http-api.md): the HTTP surface the playground uses
7106
6887
  - [Sessions and streaming](/docs/reference/sessions.md): the streams it renders
@@ -7137,7 +6918,7 @@ to the directory name. When serving multiple agents, the slug is the
7137
6918
  directory name and must match `[A-Za-z0-9][A-Za-z0-9_-]*` (and not the
7138
6919
  reserved `v1`, `playground`, or `docs` segments).
7139
6920
 
7140
- ## Project overview
6921
+ ## Project tree
7141
6922
 
7142
6923
  Most projects start with this shape.
7143
6924
 
@@ -7162,8 +6943,8 @@ my-agent/
7162
6943
  ```
7163
6944
 
7164
6945
  Evals live in `evals/` at the project root, a sibling of `agent/`, never
7165
- inside it. `agent/evals/` is silently ignored. See
7166
- [Evals](/docs/evals.md).
6946
+ inside it. `agent/evals/` is ignored, and `validate` warns about it.
6947
+ See [Evals](/docs/evals.md).
7167
6948
 
7168
6949
  ## Folder reference
7169
6950
 
@@ -7181,30 +6962,26 @@ Each path maps to a capability and a reference page.
7181
6962
  | `agent/extensions/<ns>.ts` or `agent/extensions/<ns>/` | A mounted extension or Cursor plugin; its contributions become `<ns>__<name>` | [Extensions](/docs/reference/extensions.md) |
7182
6963
  | `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) |
7183
6964
  | `agent/hooks/*.ts` | Observe-only event subscribers, never fatal | [Hooks](/docs/reference/hooks.md) |
7184
- | `agent/otel.ts` | Factory-only OTLP authoring (`defineOtel`). Public path is env / `serve({ otel })`. | [OpenTelemetry](/docs/guides/opentelemetry.md) |
7185
- | `agent/storage.ts` | `defineStorage` backend for the durable `host.kv` / `host.files` APIs | None |
7186
6965
  | `agent/artifacts.ts` | `defineArtifacts` kinds, the `tag_artifact` opt-in, and retention | [Artifacts](/docs/reference/artifacts.md) |
7187
6966
  | `agent/result.ts` | `defineResult` host `commit` on the final assistant text (`throw` or `ctx.reject`) | None |
7188
6967
  | `agent/schedules/*` | Cron-driven runs (UTC, 5-field; never auto-fire under `--dev`) | [Schedules](/docs/reference/schedules.md) |
7189
- | `agent/sandbox/workspace/**` | Seed files copied into each local session workspace | [Sessions](/docs/reference/sessions.md#what-goes-into-a-local-session-workspace) |
7190
- | `agent/playground/` | Custom playground tool chips | [Playground](/docs/reference/playground.md) |
6968
+ | `agent/reminders/*.ts` | Named reminder handlers that stay armed across restarts | [Schedules](/docs/reference/schedules.md#reminders) |
6969
+ | `agent/sandbox/workspace/**` | Seed files copied into each local session workspace | [Sessions](/docs/reference/sessions.md#local-session-workspace) |
7191
6970
  | `agent/lib/` | Import-only shared code, never discovered | None |
7192
6971
  | `evals/evals.config.ts` | Shared eval settings (e.g. `maxConcurrency`); required when evals exist | [Evals](/docs/evals.md) |
7193
6972
  | `evals/**/*.eval.ts` | Filesystem evals; case id = path under `evals/` | [Evals](/docs/evals.md) |
7194
6973
 
7195
- `agent/lib/` is the only place for shared code. Everything else under
7196
- `agent/` is discovery surface. A stray `.ts` file in one of these
7197
- folders is treated as a definition.
6974
+ Use `agent/lib/` for shared imports. A `.ts` file in one of the listed
6975
+ discovery folders loads as a definition; unrecognized directories
6976
+ produce a validation warning.
7198
6977
 
7199
- ## Why didn't the Agent SDK discover my file?
6978
+ ## Inspect discovery
7200
6979
 
7201
6980
  Run `agent-sdk validate --dir .` and `agent-sdk info --dir .`.
7202
6981
  `validate` prints diagnostics, and `serve` refuses to start on
7203
6982
  error-severity ones. Warnings, such as cloud runtime combined with
7204
6983
  local-only capabilities, print but don't block. `info` lists the discovered
7205
- surface, so a missing tool or channel shows up immediately. From
7206
- there, check the folder reference: the file is usually in the wrong directory
7207
- or has the wrong extension.
6984
+ surface, so a missing tool or channel shows up immediately.
7208
6985
 
7209
6986
  ```bash
7210
6987
  agent-sdk validate --dir . # diagnostics; non-zero exit on errors
@@ -7212,51 +6989,50 @@ agent-sdk info --dir . # human-readable surface
7212
6989
  agent-sdk info --dir . --json # machine-readable project info (same shape as GET /v1/info)
7213
6990
  ```
7214
6991
 
7215
- ## What's next
7216
-
7217
- Continue with these pages:
6992
+ ## Related
7218
6993
 
7219
6994
  - [Agent config](/docs/reference/agent-config.md): the runtime config at the root
7220
6995
  - [Tools](/docs/reference/tools.md): add typed actions under `agent/tools/`
6996
+ - [CLI](/docs/reference/cli.md): commands that discover this tree
7221
6997
 
7222
6998
  ---
7223
6999
 
7224
7000
  Source: /docs/reference/prompt.md
7225
7001
 
7226
- # `prompt`
7002
+ # Prompt strings
7227
7003
 
7228
- Authoring helper for long strings that live next to indented TypeScript:
7229
- tool descriptions, reminder `prompt` fields, GitHub channel `context`,
7230
- and error messages.
7004
+ The `prompt` template tag keeps multi-line strings aligned with the
7005
+ surrounding TypeScript while returning dedented text. Use it for tool
7006
+ descriptions, reminder prompts, channel context, and errors. Import it
7007
+ from the package root or the dedicated entrypoint:
7231
7008
 
7232
7009
  ```ts
7233
7010
  import { prompt } from "@cursor/july";
7234
7011
  // or: import { prompt } from "@cursor/july/prompt";
7235
7012
  ```
7236
7013
 
7237
- ## `prompt\`…\``
7014
+ ## Dedented strings
7238
7015
 
7239
- Returns a single dedented string. Common leading whitespace is stripped;
7240
- a leading newline after the opening backtick is dropped so the usual
7241
- multiline form stays readable in source.
7016
+ `prompt` returns one string. It removes the common leading whitespace
7017
+ and one newline immediately after the opening backtick.
7242
7018
 
7243
7019
  ```ts
7244
7020
  throw new Error(prompt`
7245
- It is outside business hours (MonFri 9am5pm ET).
7021
+ It is outside business hours (Mon-Fri 9am-5pm ET).
7246
7022
  Use request_author_approval, or pass approval=human_request.
7247
7023
  `);
7248
7024
  ```
7249
7025
 
7250
7026
  Blank lines inside the body are preserved. Relative indentation after the
7251
- common prefix is kept (handy for nested bullet lists).
7027
+ common prefix is preserved, so nested lists keep their shape.
7252
7028
 
7253
7029
  When interpolating multi-line values (for example a list of services), give
7254
7030
  those lines the same indent as the `prompt` body so dedent stays consistent.
7255
7031
 
7256
- ## `prompt.lines\`…\``
7032
+ ## Line arrays
7257
7033
 
7258
- Same dedent rules, but returns `string[]`, one entry per line. Use this
7259
- where an API wants separate lines (for example GitHub channel `context`):
7034
+ `prompt.lines` applies the same rules and returns `string[]`, with one
7035
+ entry per line. Use it when an API accepts separate context lines:
7260
7036
 
7261
7037
  ```ts
7262
7038
  context: prompt.lines`
@@ -7266,13 +7042,18 @@ context: prompt.lines`
7266
7042
  `
7267
7043
  ```
7268
7044
 
7045
+ ## Related
7046
+
7047
+ - [Tools](/docs/reference/tools.md)
7048
+ - [Channels](/docs/reference/channels.md)
7049
+
7269
7050
  ---
7270
7051
 
7271
7052
  Source: /docs/reference/schedules.md
7272
7053
 
7273
7054
  # Schedules and reminders
7274
7055
 
7275
- Two ways an agent acts without an inbound message. A schedule is
7056
+ An agent can act without an inbound message in two ways. A schedule is
7276
7057
  deploy-time cron: "every weekday at 09:00, summarize open incidents." A
7277
7058
  reminder is a runtime wake bound to one conversation: "re-check this
7278
7059
  PR's CI in two hours." Schedules live in the filesystem; reminders are
@@ -7283,10 +7064,9 @@ created by running code.
7283
7064
  Cron expressions are standard 5-field, evaluated in UTC with minute
7284
7065
  granularity.
7285
7066
 
7286
- ### Schedules in Markdown
7067
+ ### Markdown schedules
7287
7068
 
7288
- A plain markdown file with `cron:` frontmatter is a fire-and-forget
7289
- task:
7069
+ A markdown file with `cron:` frontmatter is a fire-and-forget task:
7290
7070
 
7291
7071
  ```md
7292
7072
  ---
@@ -7300,10 +7080,10 @@ Each firing starts a task-mode session: the body is the prompt, the
7300
7080
  session runs to `session.completed` or `session.failed`, and it isn't
7301
7081
  followable.
7302
7082
 
7303
- ### Schedules as handlers
7083
+ ### Schedule handlers
7304
7084
 
7305
- `defineSchedule` with a `run` handler gives you full control, most
7306
- usefully to hand the work into a channel so its delivery events fire:
7085
+ Use `defineSchedule` with a `run` handler to call tools or hand work into
7086
+ a channel so its delivery events fire:
7307
7087
 
7308
7088
  ```ts
7309
7089
  import { defineSchedule } from "@cursor/july/schedules";
@@ -7312,7 +7092,6 @@ import webhook from "../channels/webhook.js";
7312
7092
  export default defineSchedule({
7313
7093
  cron: "*/30 * * * *",
7314
7094
  async run({ receive, waitUntil, appAuth, host }) {
7315
- // optional: await host.mcp.callTool("units", "celsius_to_fahrenheit", { value: 0 });
7316
7095
  waitUntil(
7317
7096
  receive(webhook, {
7318
7097
  message:
@@ -7331,14 +7110,12 @@ schedule-scoped principal for work the agent does on its own behalf),
7331
7110
  and `host` (shared services: `host.mcp`, `host.github`, `host.slack`,
7332
7111
  `host.reminders`).
7333
7112
 
7334
- ### Dispatch and dev mode
7113
+ ### Dispatch a schedule
7335
7114
 
7336
- In production (`agent-sdk serve`), schedules fire on their cron
7337
- cadence. Disable them with `--no-schedules`. There's no cross-host
7338
- coordination, so run them in exactly one process per project.
7339
-
7340
- In dev (`serve --dev`), schedules never fire automatically. Dispatch one
7341
- by hand, exactly once, through the same path production uses:
7115
+ | Mode | What fires |
7116
+ | --- | --- |
7117
+ | Production `agent-sdk serve` | Cron cadence. `--no-schedules` disables them. Run them in exactly one process per project |
7118
+ | `serve --dev` | Nothing automatic. Dispatch by hand through the same path production uses |
7342
7119
 
7343
7120
  ```bash
7344
7121
  curl -X POST http://127.0.0.1:3000/<slug>/v1/dev/schedules/heartbeat
@@ -7350,104 +7127,67 @@ The playground can dispatch schedules in dev mode too, and
7350
7127
 
7351
7128
  ## Reminders
7352
7129
 
7353
- A reminder is created at runtime and bound to a channel continuation.
7354
- When it fires, it wakes that conversation. Recurring reminders behave
7355
- like `setInterval`, one-shots like `setTimeout`, and both are durable on
7356
- disk.
7130
+ A reminder is created at runtime and bound to a channel continuation. A
7131
+ prompt reminder wakes that conversation when it fires; a handler reminder
7132
+ runs host code, which can call `followup` to wake it. Recurring reminders
7133
+ behave like `setInterval`, and one-shots behave like `setTimeout`. Prompt
7134
+ and named-handler reminders stay armed across restarts.
7357
7135
 
7358
7136
  ```ts
7359
7137
  await handle.createReminder({
7360
7138
  purpose: "ci_recheck",
7361
7139
  channelId: "drive",
7362
- continuationToken: "pr:owner/repo#1",
7363
- delay: "2h", // or a cron / explicit schedule
7140
+ continuationToken: "pr:acme/checkout#42",
7141
+ delay: "2h",
7364
7142
  prompt: "Re-check CI. Only act if still failing.",
7365
7143
  until: "Cancel once CI is green or the PR is merged.",
7366
7144
  });
7367
7145
  ```
7368
7146
 
7369
- Use `run` when host code should decide what happens on each tick:
7147
+ When host code must decide what happens on each tick, author a named
7148
+ handler and pass its default export with serializable `args`:
7370
7149
 
7371
7150
  ```ts
7372
- await handle.createReminder({
7373
- purpose: "ci_recheck",
7374
- channelId: "drive",
7375
- continuationToken: "pr:owner/repo#1",
7376
- every: "30m",
7377
- async run({ fireCount, followup }) {
7378
- if (fireCount >= 3) {
7379
- return { action: "stop" };
7380
- }
7151
+ // agent/reminders/ci_recheck.ts
7152
+ import { defineReminder } from "@cursor/july/reminders";
7381
7153
 
7154
+ export default defineReminder({
7155
+ async run({ args, followup }) {
7382
7156
  await followup({
7383
- message: "Re-check CI and report only if the status changed.",
7157
+ message: `Re-check CI for ${String(args.prUrl)}. Report only if the status changed.`,
7384
7158
  });
7385
7159
  return { action: "delivered" };
7386
7160
  },
7387
7161
  });
7388
7162
  ```
7389
7163
 
7390
- This reminder wakes the conversation three times, then stops itself.
7391
-
7392
- The same API is `host.reminders` on channel handlers, tools, and
7393
- schedule runs. An agent can even be given a tool that creates its own
7394
- reminders.
7395
-
7396
- For example, create `agent/tools/remind_me.ts`:
7397
-
7398
7164
  ```ts
7399
- import { defineTool } from "@cursor/july/tools";
7400
- import { z } from "zod";
7401
-
7402
- export default defineTool({
7403
- description: "Schedule a one-time reminder in this conversation.",
7404
- inputSchema: z.object({
7405
- delay: z
7406
- .string()
7407
- .describe("When to wake the conversation, such as 20m or 2h"),
7408
- prompt: z
7409
- .string()
7410
- .min(1)
7411
- .describe("What the agent should do when it wakes"),
7412
- }),
7413
- async execute({ delay, prompt }, ctx) {
7414
- const reminders = ctx.host.reminders;
7415
- if (reminders === undefined) {
7416
- throw new Error("Reminders are disabled on this host.");
7417
- }
7165
+ import ciRecheck from "./agent/reminders/ci_recheck.js";
7418
7166
 
7419
- const continuationToken = ctx.session.continuationKey;
7420
- if (continuationToken == null) {
7421
- throw new Error("This session cannot receive reminder follow-ups.");
7422
- }
7423
-
7424
- const reminder = await reminders.create({
7425
- purpose: "user_follow_up",
7426
- channelId: ctx.session.channelId,
7427
- continuationToken,
7428
- delay,
7429
- prompt,
7430
- });
7431
-
7432
- return {
7433
- reminderId: reminder.id,
7434
- nextFireAt: reminder.nextFireAt,
7435
- };
7436
- },
7167
+ await handle.createReminder({
7168
+ purpose: "ci_recheck",
7169
+ channelId: "drive",
7170
+ continuationToken: "pr:acme/checkout#42",
7171
+ every: "30m",
7172
+ handler: ciRecheck,
7173
+ args: { prUrl: "https://github.com/acme/checkout/pull/42" },
7437
7174
  });
7438
7175
  ```
7439
7176
 
7440
- The tool binds the reminder to the current channel conversation. When
7441
- the delay expires, the prompt returns to the same session as a follow-up.
7177
+ Use an anonymous `run` handler only for legacy projects that can re-arm
7178
+ it after a restart. New projects should use a named handler.
7442
7179
 
7443
- Reminders fire in one of two styles. The **prompt form** (above) sends
7444
- `prompt` into the session, with `until` stating the standing
7445
- cancellation condition for the model to honor. The **run form** passes a
7446
- `run` handler instead: it returns `stop`, `skip`, or `delivered` per
7447
- tick. That's silent host-side policy with no model turn. Run handlers are
7448
- in-memory, so after a restart those reminders are disarmed
7449
- (`handler_lost_on_restart`); re-arm them from the code path that created
7450
- them, or prefer the prompt form.
7180
+ | Form | What it does | After a restart |
7181
+ | --- | --- | --- |
7182
+ | Prompt | Sends `prompt` into the session. `until` is the standing cancellation condition for the model | Stays armed |
7183
+ | Named handler | Runs a discovered handler with serializable `args`; it starts a model turn only if the handler calls `followup` | Stays armed |
7184
+ | Anonymous `run` | Host handler returns `stop`, `skip`, or `delivered` per tick; it starts a model turn only if it calls `followup` | Must be re-armed |
7185
+
7186
+ The same API is `host.reminders` on channel handlers, tools, and
7187
+ schedule runs. `builtinTools: { reminders: true }` adds
7188
+ `reminders_create`, `reminders_list`, and `reminders_cancel` on the
7189
+ current conversation; see
7190
+ [Agent config: built-in tools](/docs/reference/agent-config.md#built-in-tools).
7451
7191
 
7452
7192
  `--dev` does not auto-fire reminders. Dispatch one by hand:
7453
7193
 
@@ -7458,27 +7198,21 @@ curl -X POST http://127.0.0.1:3000/<slug>/v1/dev/reminders/<id> # fire one
7458
7198
 
7459
7199
  `handle.dispatchReminder(id)` is the programmatic equivalent.
7460
7200
 
7461
- Two habits worth copying: cancel reminders when their subject
7462
- dies (say, cancel PR-scoped reminders on `pull_request.closed`), and keep
7463
- wake prompts generic. A plain "re-check the PR" works better than
7464
- replaying stale payload details, because the agent re-reads the live
7465
- state when it wakes.
7466
-
7467
- ## Schedule or reminder?
7201
+ Cancel reminders when their subject dies, for example on
7202
+ `pull_request.closed`. Keep wake prompts generic so the agent re-reads
7203
+ live state instead of replaying a stale payload.
7468
7204
 
7469
- The split comes down to scope and timing.
7205
+ ## Schedules vs reminders
7470
7206
 
7471
7207
  | | Schedule | Reminder |
7472
7208
  | -------- | ---------------------------------------------------------- | ----------------------------------------------- |
7473
7209
  | Defined | at deploy time, `agent/schedules/*` | at runtime, `createReminder` / `host.reminders` |
7474
7210
  | Scope | global to the agent | one channel continuation (one conversation) |
7475
7211
  | Session | starts a new task session (or hands off through `receive`) | wakes an existing conversation |
7476
- | Cadence | cron (UTC) | delay, cron, or explicit schedule |
7212
+ | Cadence | cron (UTC) | `every`, `cron`, `delay`, or `at` |
7477
7213
  | Dev mode | manual dispatch only | manual dispatch only (timers off) |
7478
7214
 
7479
- ## What's next
7480
-
7481
- Continue with these pages:
7215
+ ## Related
7482
7216
 
7483
7217
  - [Channels](/docs/reference/channels.md): `receive` and the delivery events
7484
7218
  - [GitHub guide](/docs/guides/github.md): reminders in a real webhook loop
@@ -7492,9 +7226,12 @@ Source: /docs/reference/sessions.md
7492
7226
  # Sessions, events, and streaming
7493
7227
 
7494
7228
  A session keeps one conversation, its workspace, and an append-only
7495
- record of every message and tool call.
7229
+ record of every message and tool call. Continue it with a continuation
7230
+ token, inspect it with a session ID, and follow progress on the NDJSON
7231
+ stream. Chat sessions wait for follow-ups; task sessions run once and
7232
+ stop.
7496
7233
 
7497
- ## What does a session contain?
7234
+ ## Session contents
7498
7235
 
7499
7236
  Each session combines:
7500
7237
 
@@ -7502,12 +7239,12 @@ Each session combines:
7502
7239
  - A conversation the caller can continue
7503
7240
  - A workspace for local turns
7504
7241
  - An NDJSON event stream
7505
- - Runtime state needed to resume after a server restart
7242
+ - State that lets the conversation resume after a restart
7506
7243
 
7507
7244
  Sessions belong to the principal that created them. Follow-up, stream,
7508
7245
  and list routes return `403` when another caller tries to access one.
7509
7246
 
7510
- ## Which session identifier should I use?
7247
+ ## Session identifiers
7511
7248
 
7512
7249
  Sessions have two identifiers because conversation routing and
7513
7250
  inspection are different jobs.
@@ -7525,7 +7262,7 @@ accepted follow-up. Reusing a stale HTTP token returns `409`.
7525
7262
  Use the continuation token to keep talking. Use the session ID to
7526
7263
  observe or manage the stored session.
7527
7264
 
7528
- ## Which session modes are available?
7265
+ ## Session modes
7529
7266
 
7530
7267
  | Mode | Created by | What happens after a turn |
7531
7268
  | --- | --- | --- |
@@ -7534,7 +7271,7 @@ observe or manage the stored session.
7534
7271
 
7535
7272
  Task sessions don't accept follow-ups. Trying one returns `409`.
7536
7273
 
7537
- ## What happens when I send a follow-up?
7274
+ ## Follow-ups
7538
7275
 
7539
7276
  A follow-up to an idle chat session starts another turn. Admission when
7540
7277
  the session is already busy depends on the channel:
@@ -7542,23 +7279,21 @@ the session is already busy depends on the channel:
7542
7279
  | Path | Busy-session policy |
7543
7280
  | --- | --- |
7544
7281
  | HTTP playground / `POST /v1/session/:id` / MCP `ask` | **Preempt** (default): interrupt the in-flight turn, wait for it to settle, then run the new message |
7545
- | Slack mentions / DMs / alert-watch | **Coalesce**: leave the active turn running, enqueue the follow-up, and drain queued asks into one follow-up turn when the active turn finishes (no mid-turn tool/hook inject) |
7282
+ | Slack mentions / DMs / alert-watch | **Coalesce**: leave the active turn running and queue the follow-up. The running turn may receive it at a tool boundary; any asks that remain become one follow-up turn when it finishes |
7546
7283
 
7547
7284
  Pass `admission: "coalesce"` on `send()` to opt into the Slack policy from
7548
7285
  other callers. Omit it (or pass `"preempt"`) to keep interrupt semantics.
7549
7286
 
7550
7287
  `POST /v1/session/:id/stop` interrupts a turn without sending a new
7551
- message. Interrupted turns record `turn.failed` with
7552
- `"turn interrupted"`. This means the turn was preempted. A whole-message
7553
- Slack `stop` / `@agent stop` does the same for that thread and clears
7554
- pending coalesced nudges.
7288
+ message. The stream records `turn.failed` with `status: "cancelled"`;
7289
+ the session stays a chat session and can take another follow-up. A
7290
+ whole-message Slack `stop` / `@agent stop` does the same for that thread
7291
+ and clears pending coalesced follow-ups.
7555
7292
 
7556
- Session-bound deterministic tool calls share the lock only for writes: a
7557
- write-effect call returns `409 session_busy` while a model turn is
7558
- running, a read-effect call runs alongside the turn (see
7559
- [Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn)).
7293
+ See [Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn) for direct-call
7294
+ behavior while a session is busy.
7560
7295
 
7561
- ## Which events can I stream?
7296
+ ## Stream events
7562
7297
 
7563
7298
  Each NDJSON line uses this envelope:
7564
7299
  `{ type, index, sessionId, turnId?, at, data }`. The `index` increases
@@ -7580,13 +7315,11 @@ within one session. The `at` field is an ISO-8601 timestamp.
7580
7315
 
7581
7316
  Pair `actions.requested` with `action.result` to reconstruct the tool
7582
7317
  trajectory. Read `turn.completed.data.usage` for input, output, and
7583
- cache token counts. When `agent/result.ts` is authored, a thrown
7584
- `commit`, a `ctx.reject` that exhausts the two-repair budget, or empty
7585
- assistant text emits `turn.failed` instead of `turn.completed`.
7586
- `ctx.reject(message)` re-runs the same turn with that message so the
7587
- model can revise while tools and the session filesystem are still up.
7318
+ cache token counts. With `agent/result.ts`, `turn.failed` is emitted when
7319
+ `commit` throws, the turn has no assistant text, or `ctx.reject(message)`
7320
+ doesn't lead to an accepted revision.
7588
7321
 
7589
- ## How do I stream or replay session events?
7322
+ ## Stream or replay events
7590
7323
 
7591
7324
  One endpoint handles both live streaming and replay:
7592
7325
 
@@ -7594,27 +7327,27 @@ One endpoint handles both live streaming and replay:
7594
7327
  curl -N 'http://127.0.0.1:3000/<slug>/v1/session/ses_…/stream?startIndex=0'
7595
7328
  ```
7596
7329
 
7597
- Pass `startIndex` to continue after the last event you received. Omit it
7598
- or pass `0` to replay the full session before following new events.
7330
+ Pass `startIndex` to continue after the last event you received,
7331
+ including after a server restart. Omit it or pass `0` to replay the
7332
+ full session before following new events.
7599
7333
  `GET /v1/session/:id/events` returns a one-time dump without staying
7600
7334
  connected.
7601
7335
 
7602
- Event streams replay from disk after a server restart. Conversation
7603
- state resumes from the Cursor SDK store.
7604
-
7605
- ## What goes into a local session workspace?
7336
+ ## Local session workspace
7606
7337
 
7607
7338
  The Agent SDK creates a workspace before the first local turn:
7608
7339
 
7609
7340
  | Source path | Lands as |
7610
7341
  | ---------------------------------- | ----------------------------------------------------------------------- |
7611
- | `instructions.*` | `AGENTS.md` |
7612
7342
  | `skills/*` | `.cursor/skills/<name>/SKILL.md` |
7613
7343
  | agent tools (`execution: "agent"`) | scripts in the session workspace, with a catalog in `AGENTS.md` |
7614
7344
  | `sandbox/workspace/**` | copied in as seed files |
7615
7345
  | per-send `workspaceFiles` | written before the turn |
7616
7346
 
7617
- The local harness uses this workspace as its working directory. Parent
7347
+ Instructions reach the model as its system prompt; they aren't written
7348
+ into the workspace. See [Instructions](/docs/reference/instructions.md#delivery).
7349
+
7350
+ Local turns use this workspace as their working directory. Parent
7618
7351
  directories can contribute `AGENTS.md` and `.cursor` settings. Set
7619
7352
  `local.cwd` when you need a clean parent directory. A channel can also
7620
7353
  provide a different working directory for one session, such as a PR
@@ -7623,20 +7356,16 @@ worktree.
7623
7356
  See [Agent config: local cwd](/docs/reference/agent-config.md#local-cwd) for the
7624
7357
  inheritance rules.
7625
7358
 
7626
- ## Where does the Agent SDK store session data?
7359
+ ## Session storage
7627
7360
 
7628
- Local state lives under `--state-root`. Slugged mounts store it under
7629
- a subdirectory named for the slug.
7361
+ Local session state lives under `--state-root`. A slugged mount stores
7362
+ it in a subdirectory named for the slug.
7630
7363
 
7631
7364
  Deleting a session directory removes the session from the server: it
7632
7365
  disappears from listings and can no longer be streamed or continued.
7633
7366
  Cloud conversations remain on the Cursor backend.
7634
7367
 
7635
- Nested git checkouts already default `local.cwd` outside the enclosing
7636
- repo. See
7637
- [What goes into a local session workspace?](#what-goes-into-a-local-session-workspace).
7638
-
7639
- ## How do I inspect a saved event stream?
7368
+ ## Inspect a saved event stream
7640
7369
 
7641
7370
  Use `trajectory` with a saved trace:
7642
7371
 
@@ -7645,13 +7374,14 @@ agent-sdk trajectory --events <state-root>/traces/<sessionId>.ndjson
7645
7374
  ```
7646
7375
 
7647
7376
  The command prints tool calls, the reply, and token usage in the same
7648
- JSON shape as `run`. Use **Open trace** in the playground for a visual
7649
- view.
7377
+ JSON shape as `run`. Open the session's **Trace** view in the playground
7378
+ for a visual timeline.
7650
7379
 
7651
7380
  ## Related
7652
7381
 
7653
7382
  - [HTTP API](/docs/reference/http-api.md)
7654
7383
  - [Hooks](/docs/reference/hooks.md)
7384
+ - [Tools](/docs/reference/tools.md)
7655
7385
 
7656
7386
  ---
7657
7387
 
@@ -7667,15 +7397,15 @@ always-on [instructions](/docs/reference/instructions.md).
7667
7397
 
7668
7398
  ## Authoring forms
7669
7399
 
7670
- Three forms cover every case.
7400
+ Skills support three authoring forms.
7671
7401
 
7672
7402
  | Form | Reach for it when |
7673
7403
  | --- | --- |
7674
- | `agent/skills/<name>.md` | Flat markdown. Optional `description` frontmatter; the first body line is the fallback. |
7404
+ | `agent/skills/<name>.md` | Static Markdown. Optional `description` frontmatter; the first body line is the fallback. |
7675
7405
  | `agent/skills/<name>/SKILL.md` plus siblings | A packaged directory with reference files (`references/…`). Requires `description` frontmatter. |
7676
7406
  | `agent/skills/<name>.ts` | Generated content, with `defineSkill` from `@cursor/july/skills`. |
7677
7407
 
7678
- Flat markdown:
7408
+ Use flat Markdown for static content:
7679
7409
 
7680
7410
  ```md
7681
7411
  ---
@@ -7689,8 +7419,8 @@ description: Use when a pull request needs a structured approval checklist.
7689
7419
  3. Call `approve_pr` only after an explicit request; it requires approval.
7690
7420
  ```
7691
7421
 
7692
- TypeScript, when the content must be generated or carry inline sibling
7693
- files:
7422
+ Use TypeScript when the content must be generated or include inline
7423
+ sibling files:
7694
7424
 
7695
7425
  ```ts
7696
7426
  import { defineSkill } from "@cursor/july/skills";
@@ -7702,40 +7432,29 @@ export default defineSkill({
7702
7432
  });
7703
7433
  ```
7704
7434
 
7705
- ## How skills reach the model
7706
-
7707
- On the local runtime, skills land in the session workspace at
7708
- `.cursor/skills/<name>/SKILL.md`, and the harness advertises and loads
7709
- them natively. On the cloud runtime there is no session workspace, so
7710
- the engine copies the same SKILL.md tree onto an Agent Store for native
7711
- discovery:
7712
-
7713
- - Hosted deployments write store-root `skills/<name>/`.
7714
- - `agent-sdk serve` / `run` with a personal `CURSOR_API_KEY` write
7715
- namespaced skills on the USER store so they cannot collide with the
7716
- user's own skills.
7435
+ ## Skill delivery
7717
7436
 
7718
- `validate` still warns about the combination so the store path is
7719
- visible. Cloud turns with neither a hosted store nor an API key see
7720
- only skills already in the cloud repo.
7437
+ Local turns receive authored skills automatically. Hosted deployments
7438
+ and local `serve` or `run` processes with a personal `CURSOR_API_KEY`
7439
+ also make them available to cloud turns. Otherwise, a cloud turn sees
7440
+ only skills already in its checkout. `agent-sdk validate` warns when
7441
+ `runtime: "cloud"` is combined with authored skills.
7721
7442
 
7722
- ## Instructions, skills, or tools?
7443
+ ## Skills, instructions, and tools
7723
7444
 
7724
7445
  Instructions are always in context: identity, tool-choice rules, the
7725
7446
  output contract. Keep them short. Skills load when relevant: procedures,
7726
7447
  checklists, house style. Reach for a skill when the model needs to
7727
- *follow* something but only sometimes needs it loaded. Tools are typed,
7448
+ follow something but only sometimes needs it loaded. Tools are typed,
7728
7449
  executable behavior: anything that must be correct every time belongs in
7729
7450
  tool code, not in prose the model might paraphrase.
7730
7451
 
7731
- A good skill description is a routing rule, not a title. Say *when* to
7452
+ A good skill description is a routing rule, not a title. Say when to
7732
7453
  use it, like "Use when a pull request needs a structured approval
7733
7454
  checklist," because the description is all the model sees before
7734
7455
  deciding to load it.
7735
7456
 
7736
- ## What's next
7737
-
7738
- Continue with these pages:
7457
+ ## Related
7739
7458
 
7740
7459
  - [Instructions](/docs/reference/instructions.md): what stays always-on
7741
7460
  - [Tools](/docs/reference/tools.md): when prose needs to become code
@@ -7750,16 +7469,15 @@ Source: /docs/reference/subagents.md
7750
7469
  # Subagents
7751
7470
 
7752
7471
  A subagent is a specialist child agent the model can delegate to
7753
- mid-turn. Each one is its own directory under `agent/subagents/<id>/`,
7754
- with the same `agent.ts` + `instructions.md` shape as the root. On the
7755
- Cursor harness, subagents run as SDK custom subagents: the parent model
7756
- delegates through the harness `task` tool, and the stream records
7757
- `subagent.called` and `subagent.completed`.
7472
+ mid-turn. Each one lives under `agent/subagents/<id>/` with a required
7473
+ `agent.ts`. Add `instructions.md` or inline `instructions` in `agent.ts`
7474
+ for a custom prompt. The parent model delegates through the `task` tool,
7475
+ and the stream records `subagent.called` and `subagent.completed`.
7758
7476
 
7759
7477
  ```text
7760
7478
  agent/subagents/researcher/
7761
7479
  ├── agent.ts # description (required), model (optional)
7762
- └── instructions.md # the subagent's own system prompt
7480
+ └── instructions.md # optional custom system prompt
7763
7481
  ```
7764
7482
 
7765
7483
  ```ts
@@ -7775,47 +7493,38 @@ export default defineAgent({
7775
7493
 
7776
7494
  ## Subagent rules
7777
7495
 
7778
- `description` is required. It's the only thing the parent model reads
7779
- when deciding whether to delegate, so write it as a routing rule
7780
- ("Background research: …"), the same discipline as a
7781
- [skill](/docs/reference/skills.md) description. `model` is optional; omit it to
7782
- inherit the parent's model, or set it to run the specialist on a
7783
- different one.
7496
+ `description` is required. The parent model uses it to decide whether
7497
+ to delegate, so write it as a routing rule ("Background research: …"),
7498
+ following the same discipline as a [skill](/docs/reference/skills.md) description.
7499
+ `model` is optional; omit it to inherit the parent's model, or set it
7500
+ to run the specialist on a different model.
7784
7501
 
7785
7502
  Subagents inherit the parent's execution surface. Every per-subagent
7786
7503
  capability directory is reported as a warning and ignored: `tools/`,
7787
- `skills/`, `mcp-connections/` (and the legacy `connections/` alias),
7788
- `host-connections/`, `channels/`, `schedules/`, `hooks/`, `sandbox/`,
7789
- and nested `subagents/`.
7504
+ `skills/`, `extensions/`, `mcp-connections/`, `host-connections/`,
7505
+ `channels/`, `schedules/`, `reminders/`, `hooks/`, `sandbox/`, and
7506
+ nested `subagents/`.
7790
7507
 
7791
7508
  Delegation needs both halves: the description makes it possible, and the
7792
7509
  parent's [instructions](/docs/reference/instructions.md) make it happen. "When a
7793
7510
  request needs background research, delegate to the `researcher`
7794
- subagent."
7511
+ subagent." The parent routes; the specialist executes.
7795
7512
 
7796
- ## Subagent or peer?
7513
+ ## Subagents and peers
7797
7514
 
7798
7515
  Subagents split one job into roles inside a single agent. When the
7799
7516
  specialist is independently useful, with its own tools, sessions, and
7800
7517
  playground, make it a full agent. See
7801
7518
  [Peer agents](/docs/guides/agent-to-agent.md#peer-or-subagent).
7802
7519
 
7803
- ## Patterns
7804
-
7805
- Fan-out reviews: a PR-approval agent can delegate to two review
7806
- subagents that read a host-prepared `pr/` evidence tree and report
7807
- prioritized findings, which the parent embeds in its approval comment.
7808
-
7809
- Keep the parent lean: a subagent with focused instructions usually
7810
- works better than a longer parent prompt with conditional sections. The
7811
- parent routes; the specialist executes.
7812
-
7813
- ## What's next
7814
-
7815
- Continue with these pages:
7520
+ ## Related
7816
7521
 
7817
7522
  - [Skills](/docs/reference/skills.md): when a procedure is enough and a child agent
7818
7523
  is overkill
7524
+ - [Peer agents](/docs/guides/agent-to-agent.md): independently useful
7525
+ specialists
7526
+ - [Agent config](/docs/reference/agent-config.md#harness-tools): `"task"` on the
7527
+ parent's tool allowlist
7819
7528
 
7820
7529
  ---
7821
7530
 
@@ -7826,18 +7535,16 @@ Source: /docs/reference/tools.md
7826
7535
  A tool is a typed action the model can call: hit an API, run a query,
7827
7536
  write a file. Each file in `agent/tools/` defines one tool, and the
7828
7537
  filename becomes the tool name the model sees. Tools come in two
7829
- execution flavors: server tools run in-process on the serve host, and
7538
+ execution flavors: server tools run on the serving host, and
7830
7539
  agent tools run as scripts where the agent runs. Every server tool can
7831
7540
  also be called directly, with no model turn.
7832
7541
 
7833
7542
  ## Define a server tool
7834
7543
 
7835
- By default, `execute` runs in-process on the serving host with full
7836
- access to `process.env` and your `agent/lib/` code. Local turns call
7837
- server tools as SDK custom tools. Cloud turns reach them over
7838
- authenticated HTTP MCP back to the serve host when `--public-url` or
7839
- `--cloud-tools-url` is set; without either, the server warns at startup
7840
- and cloud turns omit them.
7544
+ By default, `execute` runs on the serving host with full access to
7545
+ `process.env` and your `agent/lib/` code. Set `--public-url` or
7546
+ `--cloud-tools-url` to make server tools available to cloud turns;
7547
+ without either, startup warns and cloud turns omit them.
7841
7548
 
7842
7549
  ```ts
7843
7550
  // agent/tools/inspect_pr.ts
@@ -7996,7 +7703,7 @@ check nothing.
7996
7703
  ## Define an agent tool
7997
7704
 
7998
7705
  Set `execution: "agent"` and the tool materializes as a shell script
7999
- that runs where the Cursor agent runs: the local harness workspace or
7706
+ that runs where the Cursor agent runs: the local session workspace or
8000
7707
  the cloud VM. The script receives JSON arguments on stdin and prints its
8001
7708
  result on stdout. Use this flavor when the tool must run next to the
8002
7709
  checkout the agent works in; on cloud, server tools stay available too
@@ -8019,23 +7726,16 @@ printf '%s\\n' "$message"
8019
7726
  ```
8020
7727
 
8021
7728
  On the local runtime, scripts land in the session workspace with a
8022
- catalog in `AGENTS.md`. On cloud, the catalog and script bodies travel
8023
- on the first prompt.
7729
+ catalog in `AGENTS.md`.
8024
7730
 
8025
7731
  ## Tools from an MCP connection, advertised by name
8026
7732
 
8027
- Authored `agent/tools/` files are one catalog for every session. When
8028
- the tools should come from an MCP server, including per-tenant toolsets
8029
- resolved at runtime, declare the connection with
8030
- `advertiseTools: true` (plus per-session `auth` when the credential
8031
- depends on who the session is for) and the engine synthesizes named 1:1
8032
- passthrough server tools from the connection's live `listTools` on every
8033
- local turn. See
8034
- [MCP Connections](/docs/reference/connections.md#advertise-tools).
8035
- Advertised tools ride the same execution path as authored server tools,
8036
- and [direct tool calls](#call-a-tool-without-a-model-turn) address them
8037
- by the same model-facing names: the call's session identity resolves the
8038
- advertised listing when the authored lookup misses.
7733
+ Set `advertiseTools: true` on a connection to expose its MCP tools by
7734
+ name. Add per-session `auth` when credentials depend on the caller.
7735
+ Model calls and [direct tool calls](#call-a-tool-without-a-model-turn)
7736
+ use the same exposed names. See
7737
+ [MCP connections](/docs/reference/connections.md#advertise-tools) for the full
7738
+ contract.
8039
7739
 
8040
7740
  ## Gate a tool on human approval
8041
7741
 
@@ -8066,8 +7766,8 @@ runtime only, and parked calls don't survive a host restart.
8066
7766
  ## Call a tool without a model turn
8067
7767
 
8068
7768
  Server tools can be called deterministically. You pick the tool and the
8069
- input. Validation and execution behave exactly as they would for a
8070
- model-initiated call, and no Cursor API key is needed.
7769
+ input. Validation and execution match a model-initiated call, and no
7770
+ Cursor API key is needed.
8071
7771
 
8072
7772
  Over HTTP, with the same auth chain as the session API:
8073
7773
 
@@ -8093,51 +7793,43 @@ agent-sdk call inspect_pr \
8093
7793
  Programmatically, `callTool(toolName, input, options?)` is available on
8094
7794
  the serve handle, on channel route handlers and `onStart` args, and on
8095
7795
  schedule `run` handlers, so a channel can mix deterministic calls with
8096
- model turns, fetching PR metadata deterministically and then `send()`ing
8097
- the review prompt:
7796
+ model turns:
8098
7797
 
8099
7798
  ```ts
8100
7799
  const outcome = await handle.callTool("inspect_pr", {
8101
7800
  prUrl: "https://github.com/acme/checkout/pull/42",
8102
7801
  });
7802
+ if (outcome.isError) {
7803
+ return outcome;
7804
+ }
8103
7805
  // { toolName, callId, isError, result, durationMs }
8104
7806
  ```
8105
7807
 
8106
- By default the call runs against an ephemeral workspace and is removed
8107
- when the call returns. Pass a `sessionId` (a body field over
8108
- HTTP, `--session` on the CLI, `options.sessionId` programmatically) to
8109
- run inside an existing session instead: the tool sees that session's
8110
- workspace, and the call is recorded on the session's event stream.
8111
- While a model turn is running, a session-bound call is admitted by its
8112
- effect: a read-effect call (a declared `effect: "read"`, or an advertised
8113
- MCP tool whose server annotates it read-only) runs alongside the turn,
8114
- reading the workspace as the turn has left it, and is recorded under its
8115
- own per-call `turnId` so trajectories keep it apart from the turn's own
8116
- calls; a write-effect call including an undeclared tool, which counts
8117
- as a write returns `409 session_busy` until the turn finishes, because
8118
- a running turn owns the workspace. When the
8119
- session's harness cwd cannot be materialized, a read-effect call runs
8120
- in a scratch workspace instead and the outcome carries
8121
- `scratchWorkspace: true`; a write-effect call fails with
8122
- `workspace_unavailable`.
8123
-
8124
- A session can also be addressed by its continuation token: an optional
8125
- `continuationToken` (`<channelId>:<key>`, as `/v1/sessions` lists it;
8126
- mutually exclusive with `sessionId`). A token that maps to a live
8127
- session behaves exactly like passing that session's id — same ownership
8128
- check, same busy semantics, same event recording. A token with no
8129
- session behind it runs the call scratch-bound with the token's channel
8130
- id and continuation key as the call's session identity, so a deployment
8131
- whose tools resolve state from the continuation key can serve it with
8132
- no live session. Malformed tokens are rejected with
8133
- `400 invalid_continuation_token`.
8134
-
8135
- The error semantics match the model path. Unknown tools are rejected
8136
- with the available names, agent-execution tools cannot be called on the
8137
- host (`400`), schema-invalid input is a `400` before the tool body runs
8138
- (Zod validates; plain JSON Schema passes through unvalidated), and a
8139
- tool body that throws reports `isError: true` in the same envelope the
8140
- model would see.
7808
+ Omit both identifiers to run against an ephemeral workspace that is
7809
+ removed when the call returns. Pass `sessionId` (HTTP body, `--session`,
7810
+ or `options.sessionId`) or `continuationToken` (`<channelId>:<key>` as
7811
+ `/v1/sessions` lists it) to bind the call. The two are mutually
7812
+ exclusive.
7813
+
7814
+ | Rule | What happens |
7815
+ | --- | --- |
7816
+ | Existing `sessionId`, or a token that maps to an existing session | Session workspace, owner check, and event recording |
7817
+ | Token with no matching session | Scratch workspace; the token's channel id and key are the call identity |
7818
+ | Busy session, `effect: "read"` (or an advertised MCP tool marked read-only) | Runs alongside the turn, reading the workspace as the turn left it; recorded under its own `turnId` |
7819
+ | Busy session, write or undeclared | `409 session_busy` until the turn finishes |
7820
+ | Session workspace cannot be created, read | Scratch workspace; the outcome has `scratchWorkspace: true` |
7821
+ | Session workspace cannot be created, write | `500 workspace_unavailable` |
7822
+
7823
+ | Error | When |
7824
+ | --- | --- |
7825
+ | `404 unknown_tool` | Name is not a callable server tool; the response lists available names |
7826
+ | `400 tool_not_callable` | Agent-execution tools cannot run on the host |
7827
+ | `400 invalid_tool_input` | Zod rejected the input before `execute`. Plain JSON Schema is forwarded unvalidated |
7828
+ | `400 invalid_continuation_token` | Malformed token, or passed together with `sessionId` |
7829
+ | `404 session_not_found` | `sessionId` does not exist |
7830
+ | `409 session_busy` | Write-effect call while a model turn is running |
7831
+ | `500 workspace_unavailable` | Write-effect call when the session workspace cannot be created |
7832
+ | `isError: true` | The tool body threw; same envelope the model would see |
8141
7833
 
8142
7834
  ## Design habits
8143
7835
 
@@ -8155,9 +7847,7 @@ wrong, no instruction change fixes it.
8155
7847
  Gate side effects with `needsApproval`. Declare each tool's `effect` so
8156
7848
  dry-run sessions can execute reads and stub writes.
8157
7849
 
8158
- ## What's next
8159
-
8160
- Continue with these pages:
7850
+ ## Related
8161
7851
 
8162
7852
  - [MCP connections](/docs/reference/connections.md): tools that come from MCP servers
8163
7853
  instead
@@ -8765,7 +8455,7 @@ Match the visible symptom below. If `agent-sdk` is not on `PATH`, use
8765
8455
  | File reads and greps fail repeatedly with `NGHTTP2_FRAME_SIZE_ERROR` | Run the agent under Node 22.13 or newer, never Bun. |
8766
8456
  | The turn immediately reports a missing API key | Set `CURSOR_API_KEY`, `CURSOR_API_KEY_FILE`, or `CURSOR_SERVICE_ACCOUNT_KEY`, or run `agent-sdk login`. |
8767
8457
  | Login succeeds but the model host rejects the key | Point login and model traffic at the same API host; check `CURSOR_API_BASE_URL` and `CURSOR_BACKEND_URL`. |
8768
- | Cloud turns cannot reach local tools or skills | Give the cloud runtime a reachable `--public-url` and protected tool bridge, or move the required capability into the cloud checkout. See [agent runtime](/docs/reference/agent-config.md#choose-a-runtime). |
8458
+ | Cloud turns cannot reach local tools or skills | Give the cloud runtime a reachable `--public-url` and protected tool bridge, or move the required capability into the cloud checkout. See [agent runtime](/docs/reference/agent-config.md#runtime). |
8769
8459
 
8770
8460
  ## A turn behaves unexpectedly
8771
8461