@cursor/july 0.1.1

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 (623) hide show
  1. package/AGENTS.md +290 -0
  2. package/LICENSE.md +3 -0
  3. package/README.md +1233 -0
  4. package/dist/ab.d.ts +290 -0
  5. package/dist/ab.d.ts.map +1 -0
  6. package/dist/ab.js +386 -0
  7. package/dist/bin/agent-serve.d.ts +15 -0
  8. package/dist/bin/agent-serve.d.ts.map +1 -0
  9. package/dist/bin/agent-serve.js +754 -0
  10. package/dist/channels/github/api.d.ts +101 -0
  11. package/dist/channels/github/api.d.ts.map +1 -0
  12. package/dist/channels/github/api.js +229 -0
  13. package/dist/channels/github/auth.d.ts +67 -0
  14. package/dist/channels/github/auth.d.ts.map +1 -0
  15. package/dist/channels/github/auth.js +239 -0
  16. package/dist/channels/github/binding.d.ts +73 -0
  17. package/dist/channels/github/binding.d.ts.map +1 -0
  18. package/dist/channels/github/binding.js +114 -0
  19. package/dist/channels/github/defaults.d.ts +24 -0
  20. package/dist/channels/github/defaults.d.ts.map +1 -0
  21. package/dist/channels/github/defaults.js +100 -0
  22. package/dist/channels/github/events.d.ts +18 -0
  23. package/dist/channels/github/events.d.ts.map +1 -0
  24. package/dist/channels/github/events.js +59 -0
  25. package/dist/channels/github/forward.d.ts +137 -0
  26. package/dist/channels/github/forward.d.ts.map +1 -0
  27. package/dist/channels/github/forward.js +271 -0
  28. package/dist/channels/github/github-channel.d.ts +127 -0
  29. package/dist/channels/github/github-channel.d.ts.map +1 -0
  30. package/dist/channels/github/github-channel.js +1688 -0
  31. package/dist/channels/github/index.d.ts +52 -0
  32. package/dist/channels/github/index.d.ts.map +1 -0
  33. package/dist/channels/github/index.js +51 -0
  34. package/dist/channels/github/replay.d.ts +115 -0
  35. package/dist/channels/github/replay.d.ts.map +1 -0
  36. package/dist/channels/github/replay.js +282 -0
  37. package/dist/channels/github/state.d.ts +26 -0
  38. package/dist/channels/github/state.d.ts.map +1 -0
  39. package/dist/channels/github/state.js +50 -0
  40. package/dist/channels/github/types.d.ts +385 -0
  41. package/dist/channels/github/types.d.ts.map +1 -0
  42. package/dist/channels/github/types.js +1 -0
  43. package/dist/channels/slack/api.d.ts +117 -0
  44. package/dist/channels/slack/api.d.ts.map +1 -0
  45. package/dist/channels/slack/api.js +437 -0
  46. package/dist/channels/slack/approvals.d.ts +32 -0
  47. package/dist/channels/slack/approvals.d.ts.map +1 -0
  48. package/dist/channels/slack/approvals.js +157 -0
  49. package/dist/channels/slack/assistant.d.ts +23 -0
  50. package/dist/channels/slack/assistant.d.ts.map +1 -0
  51. package/dist/channels/slack/assistant.js +67 -0
  52. package/dist/channels/slack/auth.d.ts +40 -0
  53. package/dist/channels/slack/auth.d.ts.map +1 -0
  54. package/dist/channels/slack/auth.js +71 -0
  55. package/dist/channels/slack/channel-watch.d.ts +46 -0
  56. package/dist/channels/slack/channel-watch.d.ts.map +1 -0
  57. package/dist/channels/slack/channel-watch.js +180 -0
  58. package/dist/channels/slack/constants.d.ts +17 -0
  59. package/dist/channels/slack/constants.d.ts.map +1 -0
  60. package/dist/channels/slack/constants.js +27 -0
  61. package/dist/channels/slack/credentials.d.ts +35 -0
  62. package/dist/channels/slack/credentials.d.ts.map +1 -0
  63. package/dist/channels/slack/credentials.js +88 -0
  64. package/dist/channels/slack/cursor-account.d.ts +76 -0
  65. package/dist/channels/slack/cursor-account.d.ts.map +1 -0
  66. package/dist/channels/slack/cursor-account.js +88 -0
  67. package/dist/channels/slack/defaults.d.ts +32 -0
  68. package/dist/channels/slack/defaults.d.ts.map +1 -0
  69. package/dist/channels/slack/defaults.js +380 -0
  70. package/dist/channels/slack/dispatch.d.ts +43 -0
  71. package/dist/channels/slack/dispatch.d.ts.map +1 -0
  72. package/dist/channels/slack/dispatch.js +185 -0
  73. package/dist/channels/slack/doctor.d.ts +24 -0
  74. package/dist/channels/slack/doctor.d.ts.map +1 -0
  75. package/dist/channels/slack/doctor.js +115 -0
  76. package/dist/channels/slack/event-id-dedupe.d.ts +10 -0
  77. package/dist/channels/slack/event-id-dedupe.d.ts.map +1 -0
  78. package/dist/channels/slack/event-id-dedupe.js +23 -0
  79. package/dist/channels/slack/inbound.d.ts +41 -0
  80. package/dist/channels/slack/inbound.d.ts.map +1 -0
  81. package/dist/channels/slack/inbound.js +166 -0
  82. package/dist/channels/slack/index.d.ts +45 -0
  83. package/dist/channels/slack/index.d.ts.map +1 -0
  84. package/dist/channels/slack/index.js +44 -0
  85. package/dist/channels/slack/init.d.ts +32 -0
  86. package/dist/channels/slack/init.d.ts.map +1 -0
  87. package/dist/channels/slack/init.js +82 -0
  88. package/dist/channels/slack/interactive.d.ts +19 -0
  89. package/dist/channels/slack/interactive.d.ts.map +1 -0
  90. package/dist/channels/slack/interactive.js +142 -0
  91. package/dist/channels/slack/links.d.ts +10 -0
  92. package/dist/channels/slack/links.d.ts.map +1 -0
  93. package/dist/channels/slack/links.js +19 -0
  94. package/dist/channels/slack/live-delivery.d.ts +75 -0
  95. package/dist/channels/slack/live-delivery.d.ts.map +1 -0
  96. package/dist/channels/slack/live-delivery.js +143 -0
  97. package/dist/channels/slack/log.d.ts +25 -0
  98. package/dist/channels/slack/log.d.ts.map +1 -0
  99. package/dist/channels/slack/log.js +47 -0
  100. package/dist/channels/slack/manifest.d.ts +57 -0
  101. package/dist/channels/slack/manifest.d.ts.map +1 -0
  102. package/dist/channels/slack/manifest.js +110 -0
  103. package/dist/channels/slack/pr-ref.d.ts +18 -0
  104. package/dist/channels/slack/pr-ref.d.ts.map +1 -0
  105. package/dist/channels/slack/pr-ref.js +36 -0
  106. package/dist/channels/slack/setup.d.ts +14 -0
  107. package/dist/channels/slack/setup.d.ts.map +1 -0
  108. package/dist/channels/slack/setup.js +86 -0
  109. package/dist/channels/slack/slack-channel.d.ts +15 -0
  110. package/dist/channels/slack/slack-channel.d.ts.map +1 -0
  111. package/dist/channels/slack/slack-channel.js +281 -0
  112. package/dist/channels/slack/socket-mode.d.ts +26 -0
  113. package/dist/channels/slack/socket-mode.d.ts.map +1 -0
  114. package/dist/channels/slack/socket-mode.js +116 -0
  115. package/dist/channels/slack/stream.d.ts +26 -0
  116. package/dist/channels/slack/stream.d.ts.map +1 -0
  117. package/dist/channels/slack/stream.js +39 -0
  118. package/dist/channels/slack/tool-approval-events.d.ts +33 -0
  119. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -0
  120. package/dist/channels/slack/tool-approval-events.js +129 -0
  121. package/dist/channels/slack/types.d.ts +326 -0
  122. package/dist/channels/slack/types.d.ts.map +1 -0
  123. package/dist/channels/slack/types.js +4 -0
  124. package/dist/channels.d.ts +116 -0
  125. package/dist/channels.d.ts.map +1 -0
  126. package/dist/channels.js +294 -0
  127. package/dist/connections.d.ts +75 -0
  128. package/dist/connections.d.ts.map +1 -0
  129. package/dist/connections.js +75 -0
  130. package/dist/docs/404.html +22 -0
  131. package/dist/docs/ab.html +87 -0
  132. package/dist/docs/assets/ab.md.COdXkces.js +63 -0
  133. package/dist/docs/assets/ab.md.COdXkces.lean.js +1 -0
  134. package/dist/docs/assets/app.DqfFEmJd.js +1 -0
  135. package/dist/docs/assets/building-with-agents.md.CJCtZCyi.js +13 -0
  136. package/dist/docs/assets/building-with-agents.md.CJCtZCyi.lean.js +1 -0
  137. package/dist/docs/assets/chunks/@localSearchIndexroot.CcVk1uKq.js +1 -0
  138. package/dist/docs/assets/chunks/VPLocalSearchBox.BaLEdS15.js +9 -0
  139. package/dist/docs/assets/chunks/framework.CAZyNGu9.js +19 -0
  140. package/dist/docs/assets/chunks/theme.CZRvu_0q.js +2 -0
  141. package/dist/docs/assets/concepts.md.Cfb9b-k1.js +4 -0
  142. package/dist/docs/assets/concepts.md.Cfb9b-k1.lean.js +1 -0
  143. package/dist/docs/assets/deployment.md.Dx1TYNk5.js +14 -0
  144. package/dist/docs/assets/deployment.md.Dx1TYNk5.lean.js +1 -0
  145. package/dist/docs/assets/evals.md.DPZ_MAnI.js +85 -0
  146. package/dist/docs/assets/evals.md.DPZ_MAnI.lean.js +1 -0
  147. package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.js +10 -0
  148. package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.lean.js +1 -0
  149. package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.js +9 -0
  150. package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.lean.js +1 -0
  151. package/dist/docs/assets/guides_github.md.DwbKhCeS.js +20 -0
  152. package/dist/docs/assets/guides_github.md.DwbKhCeS.lean.js +1 -0
  153. package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.js +28 -0
  154. package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.lean.js +1 -0
  155. package/dist/docs/assets/guides_slack.md.bv41fHfW.js +37 -0
  156. package/dist/docs/assets/guides_slack.md.bv41fHfW.lean.js +1 -0
  157. package/dist/docs/assets/guides_webhooks.md.hFTik3lf.js +50 -0
  158. package/dist/docs/assets/guides_webhooks.md.hFTik3lf.lean.js +1 -0
  159. package/dist/docs/assets/hillclimbing.md.DHNast08.js +4 -0
  160. package/dist/docs/assets/hillclimbing.md.DHNast08.lean.js +1 -0
  161. package/dist/docs/assets/index.md.BPKcj5AI.js +20 -0
  162. package/dist/docs/assets/index.md.BPKcj5AI.lean.js +1 -0
  163. package/dist/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  164. package/dist/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  165. package/dist/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  166. package/dist/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  167. package/dist/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  168. package/dist/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  169. package/dist/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  170. package/dist/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  171. package/dist/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  172. package/dist/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  173. package/dist/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  174. package/dist/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  175. package/dist/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  176. package/dist/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  177. package/dist/docs/assets/quickstart.md.tVPiGK_L.js +35 -0
  178. package/dist/docs/assets/quickstart.md.tVPiGK_L.lean.js +1 -0
  179. package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.js +34 -0
  180. package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.lean.js +1 -0
  181. package/dist/docs/assets/reference_channels.md.D7JTR03W.js +53 -0
  182. package/dist/docs/assets/reference_channels.md.D7JTR03W.lean.js +1 -0
  183. package/dist/docs/assets/reference_cli.md.DA730zCu.js +33 -0
  184. package/dist/docs/assets/reference_cli.md.DA730zCu.lean.js +1 -0
  185. package/dist/docs/assets/reference_connections.md.C3vNH_DE.js +26 -0
  186. package/dist/docs/assets/reference_connections.md.C3vNH_DE.lean.js +1 -0
  187. package/dist/docs/assets/reference_hooks.md.BCEc3MyM.js +12 -0
  188. package/dist/docs/assets/reference_hooks.md.BCEc3MyM.lean.js +1 -0
  189. package/dist/docs/assets/reference_http-api.md.DBAahtdz.js +11 -0
  190. package/dist/docs/assets/reference_http-api.md.DBAahtdz.lean.js +1 -0
  191. package/dist/docs/assets/reference_instructions.md.BC05LEQ8.js +12 -0
  192. package/dist/docs/assets/reference_instructions.md.BC05LEQ8.lean.js +1 -0
  193. package/dist/docs/assets/reference_playground.md.CPZhfYaO.js +1 -0
  194. package/dist/docs/assets/reference_playground.md.CPZhfYaO.lean.js +1 -0
  195. package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.js +31 -0
  196. package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.lean.js +1 -0
  197. package/dist/docs/assets/reference_schedules.md.D7qijxLk.js +82 -0
  198. package/dist/docs/assets/reference_schedules.md.D7qijxLk.lean.js +1 -0
  199. package/dist/docs/assets/reference_sessions.md.DdjWabnN.js +8 -0
  200. package/dist/docs/assets/reference_sessions.md.DdjWabnN.lean.js +1 -0
  201. package/dist/docs/assets/reference_skills.md.VQnlBT3Q.js +15 -0
  202. package/dist/docs/assets/reference_skills.md.VQnlBT3Q.lean.js +1 -0
  203. package/dist/docs/assets/reference_subagents.md.CIRAVcPK.js +10 -0
  204. package/dist/docs/assets/reference_subagents.md.CIRAVcPK.lean.js +1 -0
  205. package/dist/docs/assets/reference_tools.md.DF5kwlt0.js +46 -0
  206. package/dist/docs/assets/reference_tools.md.DF5kwlt0.lean.js +1 -0
  207. package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.js +1 -0
  208. package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.lean.js +1 -0
  209. package/dist/docs/assets/style.Bj-IXa5W.css +1 -0
  210. package/dist/docs/assets/troubleshooting.md.DkovjS7h.js +1 -0
  211. package/dist/docs/assets/troubleshooting.md.DkovjS7h.lean.js +1 -0
  212. package/dist/docs/building-with-agents.html +37 -0
  213. package/dist/docs/concepts.html +28 -0
  214. package/dist/docs/deployment.html +38 -0
  215. package/dist/docs/evals.html +109 -0
  216. package/dist/docs/guides/agent-to-agent.html +34 -0
  217. package/dist/docs/guides/cloud-runtime.html +33 -0
  218. package/dist/docs/guides/github.html +44 -0
  219. package/dist/docs/guides/human-in-the-loop.html +52 -0
  220. package/dist/docs/guides/slack.html +61 -0
  221. package/dist/docs/guides/webhooks.html +74 -0
  222. package/dist/docs/hashmap.json +1 -0
  223. package/dist/docs/hillclimbing.html +28 -0
  224. package/dist/docs/index.html +44 -0
  225. package/dist/docs/quickstart.html +59 -0
  226. package/dist/docs/reference/agent-config.html +58 -0
  227. package/dist/docs/reference/channels.html +77 -0
  228. package/dist/docs/reference/cli.html +57 -0
  229. package/dist/docs/reference/connections.html +50 -0
  230. package/dist/docs/reference/hooks.html +36 -0
  231. package/dist/docs/reference/http-api.html +35 -0
  232. package/dist/docs/reference/instructions.html +36 -0
  233. package/dist/docs/reference/playground.html +25 -0
  234. package/dist/docs/reference/project-layout.html +55 -0
  235. package/dist/docs/reference/schedules.html +106 -0
  236. package/dist/docs/reference/sessions.html +32 -0
  237. package/dist/docs/reference/skills.html +39 -0
  238. package/dist/docs/reference/subagents.html +34 -0
  239. package/dist/docs/reference/tools.html +70 -0
  240. package/dist/docs/scaffolding-agents.html +25 -0
  241. package/dist/docs/troubleshooting.html +25 -0
  242. package/dist/docs/vp-icons.css +0 -0
  243. package/dist/evals.d.ts +312 -0
  244. package/dist/evals.d.ts.map +1 -0
  245. package/dist/evals.js +254 -0
  246. package/dist/hooks.d.ts +23 -0
  247. package/dist/hooks.d.ts.map +1 -0
  248. package/dist/hooks.js +23 -0
  249. package/dist/index.d.ts +71 -0
  250. package/dist/index.d.ts.map +1 -0
  251. package/dist/index.js +101 -0
  252. package/dist/internal/ab-collector.d.ts +42 -0
  253. package/dist/internal/ab-collector.d.ts.map +1 -0
  254. package/dist/internal/ab-collector.js +153 -0
  255. package/dist/internal/ab-fold.d.ts +36 -0
  256. package/dist/internal/ab-fold.d.ts.map +1 -0
  257. package/dist/internal/ab-fold.js +171 -0
  258. package/dist/internal/ab-snapshot.d.ts +70 -0
  259. package/dist/internal/ab-snapshot.d.ts.map +1 -0
  260. package/dist/internal/ab-snapshot.js +208 -0
  261. package/dist/internal/approval-gate.d.ts +34 -0
  262. package/dist/internal/approval-gate.d.ts.map +1 -0
  263. package/dist/internal/approval-gate.js +85 -0
  264. package/dist/internal/auth.d.ts +8 -0
  265. package/dist/internal/auth.d.ts.map +1 -0
  266. package/dist/internal/auth.js +27 -0
  267. package/dist/internal/authored-alias-hooks.d.ts +31 -0
  268. package/dist/internal/authored-alias-hooks.d.ts.map +1 -0
  269. package/dist/internal/authored-alias-hooks.js +43 -0
  270. package/dist/internal/authored-loaders.d.ts +25 -0
  271. package/dist/internal/authored-loaders.d.ts.map +1 -0
  272. package/dist/internal/authored-loaders.js +129 -0
  273. package/dist/internal/bounded-int.d.ts +11 -0
  274. package/dist/internal/bounded-int.d.ts.map +1 -0
  275. package/dist/internal/bounded-int.js +13 -0
  276. package/dist/internal/brand.d.ts +6 -0
  277. package/dist/internal/brand.d.ts.map +1 -0
  278. package/dist/internal/brand.js +17 -0
  279. package/dist/internal/chat-client.d.ts +19 -0
  280. package/dist/internal/chat-client.d.ts.map +1 -0
  281. package/dist/internal/chat-client.js +327 -0
  282. package/dist/internal/cli-ax.d.ts +58 -0
  283. package/dist/internal/cli-ax.d.ts.map +1 -0
  284. package/dist/internal/cli-ax.js +345 -0
  285. package/dist/internal/cli-cursor.d.ts +20 -0
  286. package/dist/internal/cli-cursor.d.ts.map +1 -0
  287. package/dist/internal/cli-cursor.js +227 -0
  288. package/dist/internal/cli-github.d.ts +47 -0
  289. package/dist/internal/cli-github.d.ts.map +1 -0
  290. package/dist/internal/cli-github.js +644 -0
  291. package/dist/internal/cli-slack.d.ts +18 -0
  292. package/dist/internal/cli-slack.d.ts.map +1 -0
  293. package/dist/internal/cli-slack.js +144 -0
  294. package/dist/internal/cloud-merge.d.ts +23 -0
  295. package/dist/internal/cloud-merge.d.ts.map +1 -0
  296. package/dist/internal/cloud-merge.js +28 -0
  297. package/dist/internal/cron.d.ts +17 -0
  298. package/dist/internal/cron.d.ts.map +1 -0
  299. package/dist/internal/cron.js +60 -0
  300. package/dist/internal/cursor/account-mcp.d.ts +95 -0
  301. package/dist/internal/cursor/account-mcp.d.ts.map +1 -0
  302. package/dist/internal/cursor/account-mcp.js +342 -0
  303. package/dist/internal/cursor/backend-client.d.ts +118 -0
  304. package/dist/internal/cursor/backend-client.d.ts.map +1 -0
  305. package/dist/internal/cursor/backend-client.js +477 -0
  306. package/dist/internal/cursor/credentials.d.ts +112 -0
  307. package/dist/internal/cursor/credentials.d.ts.map +1 -0
  308. package/dist/internal/cursor/credentials.js +329 -0
  309. package/dist/internal/cursor-event-relay.d.ts +111 -0
  310. package/dist/internal/cursor-event-relay.d.ts.map +1 -0
  311. package/dist/internal/cursor-event-relay.js +181 -0
  312. package/dist/internal/cursor-relay-core.d.ts +113 -0
  313. package/dist/internal/cursor-relay-core.d.ts.map +1 -0
  314. package/dist/internal/cursor-relay-core.js +364 -0
  315. package/dist/internal/cursor-slack-relay.d.ts +94 -0
  316. package/dist/internal/cursor-slack-relay.d.ts.map +1 -0
  317. package/dist/internal/cursor-slack-relay.js +171 -0
  318. package/dist/internal/describe-error.d.ts +3 -0
  319. package/dist/internal/describe-error.d.ts.map +1 -0
  320. package/dist/internal/describe-error.js +4 -0
  321. package/dist/internal/dev-mode.d.ts +13 -0
  322. package/dist/internal/dev-mode.d.ts.map +1 -0
  323. package/dist/internal/dev-mode.js +17 -0
  324. package/dist/internal/discovery.d.ts +55 -0
  325. package/dist/internal/discovery.d.ts.map +1 -0
  326. package/dist/internal/discovery.js +1427 -0
  327. package/dist/internal/distribution.d.ts +47 -0
  328. package/dist/internal/distribution.d.ts.map +1 -0
  329. package/dist/internal/distribution.js +82 -0
  330. package/dist/internal/docs-site.d.ts +28 -0
  331. package/dist/internal/docs-site.d.ts.map +1 -0
  332. package/dist/internal/docs-site.js +169 -0
  333. package/dist/internal/duration.d.ts +8 -0
  334. package/dist/internal/duration.d.ts.map +1 -0
  335. package/dist/internal/duration.js +35 -0
  336. package/dist/internal/eval-run-store.d.ts +81 -0
  337. package/dist/internal/eval-run-store.d.ts.map +1 -0
  338. package/dist/internal/eval-run-store.js +396 -0
  339. package/dist/internal/eval-runner.d.ts +76 -0
  340. package/dist/internal/eval-runner.d.ts.map +1 -0
  341. package/dist/internal/eval-runner.js +336 -0
  342. package/dist/internal/event-log.d.ts +51 -0
  343. package/dist/internal/event-log.d.ts.map +1 -0
  344. package/dist/internal/event-log.js +167 -0
  345. package/dist/internal/event-mapper.d.ts +46 -0
  346. package/dist/internal/event-mapper.d.ts.map +1 -0
  347. package/dist/internal/event-mapper.js +226 -0
  348. package/dist/internal/frontmatter.d.ts +12 -0
  349. package/dist/internal/frontmatter.d.ts.map +1 -0
  350. package/dist/internal/frontmatter.js +61 -0
  351. package/dist/internal/github-fanout.d.ts +32 -0
  352. package/dist/internal/github-fanout.d.ts.map +1 -0
  353. package/dist/internal/github-fanout.js +122 -0
  354. package/dist/internal/handleAgentServeTrigger.d.ts +59 -0
  355. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -0
  356. package/dist/internal/handleAgentServeTrigger.js +115 -0
  357. package/dist/internal/host-platforms.d.ts +40 -0
  358. package/dist/internal/host-platforms.d.ts.map +1 -0
  359. package/dist/internal/host-platforms.js +113 -0
  360. package/dist/internal/http-channel.d.ts +54 -0
  361. package/dist/internal/http-channel.d.ts.map +1 -0
  362. package/dist/internal/http-channel.js +382 -0
  363. package/dist/internal/ids.d.ts +19 -0
  364. package/dist/internal/ids.d.ts.map +1 -0
  365. package/dist/internal/ids.js +34 -0
  366. package/dist/internal/init-project.d.ts +22 -0
  367. package/dist/internal/init-project.d.ts.map +1 -0
  368. package/dist/internal/init-project.js +125 -0
  369. package/dist/internal/json-dir-store.d.ts +32 -0
  370. package/dist/internal/json-dir-store.d.ts.map +1 -0
  371. package/dist/internal/json-dir-store.js +100 -0
  372. package/dist/internal/json-schema.d.ts +18 -0
  373. package/dist/internal/json-schema.d.ts.map +1 -0
  374. package/dist/internal/json-schema.js +53 -0
  375. package/dist/internal/log-ring.d.ts +23 -0
  376. package/dist/internal/log-ring.d.ts.map +1 -0
  377. package/dist/internal/log-ring.js +42 -0
  378. package/dist/internal/map-pool.d.ts +6 -0
  379. package/dist/internal/map-pool.d.ts.map +1 -0
  380. package/dist/internal/map-pool.js +41 -0
  381. package/dist/internal/mcp-endpoint.d.ts +59 -0
  382. package/dist/internal/mcp-endpoint.d.ts.map +1 -0
  383. package/dist/internal/mcp-endpoint.js +355 -0
  384. package/dist/internal/mcp-host.d.ts +28 -0
  385. package/dist/internal/mcp-host.d.ts.map +1 -0
  386. package/dist/internal/mcp-host.js +162 -0
  387. package/dist/internal/net.d.ts +5 -0
  388. package/dist/internal/net.d.ts.map +1 -0
  389. package/dist/internal/net.js +30 -0
  390. package/dist/internal/peer-connections.d.ts +44 -0
  391. package/dist/internal/peer-connections.d.ts.map +1 -0
  392. package/dist/internal/peer-connections.js +67 -0
  393. package/dist/internal/playground/static.d.ts +35 -0
  394. package/dist/internal/playground/static.d.ts.map +1 -0
  395. package/dist/internal/playground/static.js +162 -0
  396. package/dist/internal/playground/toolchain.d.ts +57 -0
  397. package/dist/internal/playground/toolchain.d.ts.map +1 -0
  398. package/dist/internal/playground/toolchain.js +226 -0
  399. package/dist/internal/reminder-runner.d.ts +62 -0
  400. package/dist/internal/reminder-runner.d.ts.map +1 -0
  401. package/dist/internal/reminder-runner.js +582 -0
  402. package/dist/internal/reminder-schedule.d.ts +22 -0
  403. package/dist/internal/reminder-schedule.d.ts.map +1 -0
  404. package/dist/internal/reminder-schedule.js +82 -0
  405. package/dist/internal/reminder-store.d.ts +58 -0
  406. package/dist/internal/reminder-store.d.ts.map +1 -0
  407. package/dist/internal/reminder-store.js +106 -0
  408. package/dist/internal/resolved-connections.d.ts +41 -0
  409. package/dist/internal/resolved-connections.d.ts.map +1 -0
  410. package/dist/internal/resolved-connections.js +73 -0
  411. package/dist/internal/review-comments.d.ts +50 -0
  412. package/dist/internal/review-comments.d.ts.map +1 -0
  413. package/dist/internal/review-comments.js +194 -0
  414. package/dist/internal/router.d.ts +33 -0
  415. package/dist/internal/router.d.ts.map +1 -0
  416. package/dist/internal/router.js +93 -0
  417. package/dist/internal/run-client.d.ts +49 -0
  418. package/dist/internal/run-client.d.ts.map +1 -0
  419. package/dist/internal/run-client.js +208 -0
  420. package/dist/internal/schedule-runner.d.ts +31 -0
  421. package/dist/internal/schedule-runner.d.ts.map +1 -0
  422. package/dist/internal/schedule-runner.js +126 -0
  423. package/dist/internal/sdk-runner.d.ts +41 -0
  424. package/dist/internal/sdk-runner.d.ts.map +1 -0
  425. package/dist/internal/sdk-runner.js +221 -0
  426. package/dist/internal/server.d.ts +25 -0
  427. package/dist/internal/server.d.ts.map +1 -0
  428. package/dist/internal/server.js +1374 -0
  429. package/dist/internal/session-engine.d.ts +287 -0
  430. package/dist/internal/session-engine.d.ts.map +1 -0
  431. package/dist/internal/session-engine.js +1377 -0
  432. package/dist/internal/session-store.d.ts +29 -0
  433. package/dist/internal/session-store.d.ts.map +1 -0
  434. package/dist/internal/session-store.js +140 -0
  435. package/dist/internal/session-urls.d.ts +10 -0
  436. package/dist/internal/session-urls.d.ts.map +1 -0
  437. package/dist/internal/session-urls.js +25 -0
  438. package/dist/internal/stream-progress.d.ts +14 -0
  439. package/dist/internal/stream-progress.d.ts.map +1 -0
  440. package/dist/internal/stream-progress.js +195 -0
  441. package/dist/internal/tool-result.d.ts +20 -0
  442. package/dist/internal/tool-result.d.ts.map +1 -0
  443. package/dist/internal/tool-result.js +45 -0
  444. package/dist/internal/trajectory.d.ts +56 -0
  445. package/dist/internal/trajectory.d.ts.map +1 -0
  446. package/dist/internal/trajectory.js +248 -0
  447. package/dist/internal/workspace.d.ts +42 -0
  448. package/dist/internal/workspace.d.ts.map +1 -0
  449. package/dist/internal/workspace.js +199 -0
  450. package/dist/playground/assets/cursor-icons-outline-oY2V_mvK.woff2 +0 -0
  451. package/dist/playground/assets/index-1K-hG-7p.css +1 -0
  452. package/dist/playground/assets/index-FlWjhg3x.js +79 -0
  453. package/dist/playground/index.html +17 -0
  454. package/dist/reminders.d.ts +23 -0
  455. package/dist/reminders.d.ts.map +1 -0
  456. package/dist/reminders.js +22 -0
  457. package/dist/schedules.d.ts +23 -0
  458. package/dist/schedules.d.ts.map +1 -0
  459. package/dist/schedules.js +26 -0
  460. package/dist/skills.d.ts +22 -0
  461. package/dist/skills.d.ts.map +1 -0
  462. package/dist/skills.js +22 -0
  463. package/dist/tools.d.ts +92 -0
  464. package/dist/tools.d.ts.map +1 -0
  465. package/dist/tools.js +68 -0
  466. package/dist/types.d.ts +1609 -0
  467. package/dist/types.d.ts.map +1 -0
  468. package/dist/types.js +80 -0
  469. package/docs/README.md +174 -0
  470. package/docs/ab.md +355 -0
  471. package/docs/building-with-agents.md +129 -0
  472. package/docs/concepts.md +177 -0
  473. package/docs/deployment.md +218 -0
  474. package/docs/evals.md +383 -0
  475. package/docs/guides/agent-to-agent.md +118 -0
  476. package/docs/guides/cloud-runtime.md +122 -0
  477. package/docs/guides/github.md +172 -0
  478. package/docs/guides/human-in-the-loop.md +131 -0
  479. package/docs/guides/slack.md +245 -0
  480. package/docs/guides/webhooks.md +201 -0
  481. package/docs/hillclimbing.md +93 -0
  482. package/docs/quickstart.md +183 -0
  483. package/docs/reference/agent-config.md +154 -0
  484. package/docs/reference/channels.md +206 -0
  485. package/docs/reference/cli.md +257 -0
  486. package/docs/reference/connections.md +137 -0
  487. package/docs/reference/hooks.md +74 -0
  488. package/docs/reference/http-api.md +219 -0
  489. package/docs/reference/instructions.md +77 -0
  490. package/docs/reference/playground.md +67 -0
  491. package/docs/reference/project-layout.md +119 -0
  492. package/docs/reference/schedules.md +220 -0
  493. package/docs/reference/sessions.md +165 -0
  494. package/docs/reference/skills.md +79 -0
  495. package/docs/reference/subagents.md +74 -0
  496. package/docs/reference/tools.md +203 -0
  497. package/docs/scaffolding-agents.md +113 -0
  498. package/docs/troubleshooting.md +95 -0
  499. package/package.json +185 -0
  500. package/skills/ab/SKILL.md +139 -0
  501. package/skills/create-agent/SKILL.md +219 -0
  502. package/skills/debug/SKILL.md +71 -0
  503. package/skills/evals/SKILL.md +217 -0
  504. package/skills/framework-map/SKILL.md +142 -0
  505. package/skills/github/SKILL.md +144 -0
  506. package/skills/hillclimb/SKILL.md +162 -0
  507. package/skills/setup-slack/SKILL.md +180 -0
  508. package/src/ab.ts +675 -0
  509. package/src/bin/agent-serve.ts +949 -0
  510. package/src/channels/github/api.ts +374 -0
  511. package/src/channels/github/auth.ts +275 -0
  512. package/src/channels/github/binding.ts +223 -0
  513. package/src/channels/github/defaults.ts +111 -0
  514. package/src/channels/github/events.ts +72 -0
  515. package/src/channels/github/forward.ts +381 -0
  516. package/src/channels/github/github-channel.ts +2270 -0
  517. package/src/channels/github/index.ts +52 -0
  518. package/src/channels/github/replay.ts +387 -0
  519. package/src/channels/github/state.ts +95 -0
  520. package/src/channels/github/types.ts +490 -0
  521. package/src/channels/slack/api.ts +580 -0
  522. package/src/channels/slack/approvals.ts +198 -0
  523. package/src/channels/slack/assistant.ts +76 -0
  524. package/src/channels/slack/auth.ts +112 -0
  525. package/src/channels/slack/channel-watch.ts +219 -0
  526. package/src/channels/slack/constants.ts +30 -0
  527. package/src/channels/slack/credentials.ts +90 -0
  528. package/src/channels/slack/cursor-account.ts +166 -0
  529. package/src/channels/slack/defaults.ts +450 -0
  530. package/src/channels/slack/dispatch.ts +293 -0
  531. package/src/channels/slack/doctor.ts +135 -0
  532. package/src/channels/slack/event-id-dedupe.ts +30 -0
  533. package/src/channels/slack/inbound.ts +219 -0
  534. package/src/channels/slack/index.ts +45 -0
  535. package/src/channels/slack/init.ts +133 -0
  536. package/src/channels/slack/interactive.ts +194 -0
  537. package/src/channels/slack/links.ts +23 -0
  538. package/src/channels/slack/live-delivery.ts +201 -0
  539. package/src/channels/slack/log.ts +83 -0
  540. package/src/channels/slack/manifest.ts +179 -0
  541. package/src/channels/slack/pr-ref.ts +50 -0
  542. package/src/channels/slack/setup.ts +97 -0
  543. package/src/channels/slack/slack-channel.ts +374 -0
  544. package/src/channels/slack/socket-mode.ts +154 -0
  545. package/src/channels/slack/stream.ts +63 -0
  546. package/src/channels/slack/tool-approval-events.ts +136 -0
  547. package/src/channels/slack/types.ts +349 -0
  548. package/src/channels.ts +385 -0
  549. package/src/connections.ts +89 -0
  550. package/src/evals.ts +501 -0
  551. package/src/hooks.ts +33 -0
  552. package/src/index.ts +184 -0
  553. package/src/internal/ab-collector.ts +210 -0
  554. package/src/internal/ab-fold.ts +228 -0
  555. package/src/internal/ab-snapshot.ts +335 -0
  556. package/src/internal/approval-gate.ts +115 -0
  557. package/src/internal/auth.ts +39 -0
  558. package/src/internal/authored-alias-hooks.ts +69 -0
  559. package/src/internal/authored-loaders.ts +138 -0
  560. package/src/internal/bounded-int.ts +27 -0
  561. package/src/internal/brand.ts +29 -0
  562. package/src/internal/chat-client.ts +407 -0
  563. package/src/internal/cli-ax.ts +474 -0
  564. package/src/internal/cli-cursor.ts +275 -0
  565. package/src/internal/cli-github.ts +849 -0
  566. package/src/internal/cli-slack.ts +175 -0
  567. package/src/internal/cloud-merge.ts +41 -0
  568. package/src/internal/cron.ts +78 -0
  569. package/src/internal/cursor/account-mcp.ts +472 -0
  570. package/src/internal/cursor/backend-client.ts +614 -0
  571. package/src/internal/cursor/credentials.ts +401 -0
  572. package/src/internal/cursor-event-relay.ts +281 -0
  573. package/src/internal/cursor-relay-core.ts +444 -0
  574. package/src/internal/cursor-slack-relay.ts +242 -0
  575. package/src/internal/describe-error.ts +4 -0
  576. package/src/internal/dev-mode.ts +20 -0
  577. package/src/internal/discovery.ts +1709 -0
  578. package/src/internal/distribution.ts +97 -0
  579. package/src/internal/docs-site.ts +191 -0
  580. package/src/internal/duration.ts +39 -0
  581. package/src/internal/eval-run-store.ts +519 -0
  582. package/src/internal/eval-runner.ts +522 -0
  583. package/src/internal/event-log.ts +175 -0
  584. package/src/internal/event-mapper.ts +272 -0
  585. package/src/internal/frontmatter.ts +77 -0
  586. package/src/internal/github-fanout.ts +159 -0
  587. package/src/internal/handleAgentServeTrigger.ts +156 -0
  588. package/src/internal/host-platforms.ts +144 -0
  589. package/src/internal/http-channel.ts +546 -0
  590. package/src/internal/ids.ts +44 -0
  591. package/src/internal/init-project.ts +165 -0
  592. package/src/internal/json-dir-store.ts +109 -0
  593. package/src/internal/json-schema.ts +69 -0
  594. package/src/internal/log-ring.ts +59 -0
  595. package/src/internal/map-pool.ts +32 -0
  596. package/src/internal/mcp-endpoint.ts +518 -0
  597. package/src/internal/mcp-host.ts +211 -0
  598. package/src/internal/net.ts +31 -0
  599. package/src/internal/peer-connections.ts +100 -0
  600. package/src/internal/playground/static.ts +189 -0
  601. package/src/internal/playground/toolchain.ts +277 -0
  602. package/src/internal/reminder-runner.ts +756 -0
  603. package/src/internal/reminder-schedule.ts +106 -0
  604. package/src/internal/reminder-store.ts +145 -0
  605. package/src/internal/resolved-connections.ts +145 -0
  606. package/src/internal/review-comments.ts +268 -0
  607. package/src/internal/router.ts +145 -0
  608. package/src/internal/run-client.ts +320 -0
  609. package/src/internal/schedule-runner.ts +161 -0
  610. package/src/internal/sdk-runner.ts +315 -0
  611. package/src/internal/server.ts +1737 -0
  612. package/src/internal/session-engine.ts +1810 -0
  613. package/src/internal/session-store.ts +147 -0
  614. package/src/internal/session-urls.ts +40 -0
  615. package/src/internal/stream-progress.ts +254 -0
  616. package/src/internal/tool-result.ts +54 -0
  617. package/src/internal/trajectory.ts +306 -0
  618. package/src/internal/workspace.ts +223 -0
  619. package/src/reminders.ts +35 -0
  620. package/src/schedules.ts +37 -0
  621. package/src/skills.ts +27 -0
  622. package/src/tools.ts +127 -0
  623. package/src/types.ts +1848 -0
package/README.md ADDED
@@ -0,0 +1,1233 @@
1
+ # @cursor/july
2
+
3
+ > [!WARNING]
4
+ > **Early alpha.** `@cursor/july` is the codename release of Cursor's agent
5
+ > framework (the CLI is `agentkit`). It is under active development: APIs,
6
+ > the CLI surface, and the package name itself will change before a stable
7
+ > release, and 0.x versions may ship breaking changes without notice.
8
+
9
+ A filesystem-first framework for building and serving Cursor agents.
10
+ Customers define an agent as ordinary files — markdown for prose,
11
+ TypeScript for typed behavior — under an `agent/` directory. The framework
12
+ discovers those files, compiles them into a manifest, and serves the agent
13
+ over channels, using the Cursor SDK (`@cursor/sdk`) and the Cursor harness
14
+ as the execution engine.
15
+
16
+ **User-facing documentation lives in [`docs/`](./docs/README.md)** —
17
+ also served at `/docs` by every running `agent-serve serve` host —
18
+ getting started, concepts, guides (Slack, GitHub webhooks, approvals,
19
+ agent-to-agent, cloud runtime), evals, live A/B metrics, hillclimbing,
20
+ deployment, troubleshooting, and reference pages for each folder. This
21
+ README is the compact package reference.
22
+
23
+ Instead of one large configuration object, each part of the agent gets a
24
+ clear home. Instructions go in one file, tools in one folder, channels in
25
+ another; a file's location says what it does, and its path gives it its
26
+ name. There is no registry to keep in sync: add the file and it is
27
+ discovered, move or rename it and its identity moves with it.
28
+
29
+ ```text
30
+ my-agent/
31
+ ├── package.json
32
+ └── agent/
33
+ ├── agent.ts # runtime config: the Cursor model
34
+ ├── instructions.md # the always-on system prompt
35
+ ├── tools/
36
+ │ └── get_weather.ts # one typed tool per file
37
+ ├── skills/
38
+ │ └── forecast.md # on-demand procedures (SKILL.md convention)
39
+ ├── mcp-connections/
40
+ │ └── linear.ts # tools from external MCP servers
41
+ ├── subagents/
42
+ │ └── researcher/ # specialist child agents
43
+ ├── channels/
44
+ │ └── webhook.ts # HTTP surfaces beyond the built-in session API
45
+ ├── hooks/
46
+ │ └── audit.ts # observe the runtime event stream
47
+ ├── ab.ts # optional live A/B experiment
48
+ ├── ab/ # optional: more experiments
49
+ ├── schedules/
50
+ │ └── heartbeat.md # cron-driven runs
51
+ ├── sandbox/workspace/ # files seeded into each session's workspace
52
+ └── lib/ # shared code (import-only, never discovered)
53
+ ```
54
+
55
+ Scaffold a starter project with `agent-serve init ./my-agent` — a minimal
56
+ agent (`package.json`, `agent/agent.ts`, `agent/instructions.md`, and a demo
57
+ `agent/tools/echo.ts`) that serves immediately. From there, grow it folder by
58
+ folder; the [docs](./docs/README.md) walk a weather agent from a single
59
+ `get_weather` tool through skills, channels, evals, and deployment.
60
+
61
+ Serve one project with `agent-serve serve --dir ./my-agent --dev`, or point
62
+ `serve` at a folder of agent projects to mount every child under its
63
+ directory name.
64
+
65
+ ## Node only — do not run under Bun
66
+
67
+ Run agent-serve with **Node 22+** (from source: `pnpm exec tsx
68
+ src/bin/agent-serve.ts …`, or the built `dist/bin/agent-serve.js`). Do not
69
+ run it under Bun: Bun's HTTP/2 client corrupts the Cursor SDK's local
70
+ harness tool-result streams (`NGHTTP2_FRAME_SIZE_ERROR`), so every built-in
71
+ read/grep the model makes fails and turns degrade into failed-read retry
72
+ loops (we measured an 8-minute review that takes ~1 minute under Node).
73
+ The `mise` tasks in this package already use tsx.
74
+
75
+ ## Serving many agents at once
76
+
77
+ Point `serve` at a **folder of agent projects** and it hosts all of them on
78
+ one port, each under its own slug (its directory name):
79
+
80
+ ```bash
81
+ agent-serve serve --dir ./agents --dev
82
+ # 2 agents listening on http://127.0.0.1:3000
83
+ # index: http://127.0.0.1:3000/
84
+ # /weather-agent → /weather-agent/playground, /weather-agent/v1/*
85
+ # /concierge → /concierge/playground, /concierge/v1/*
86
+ ```
87
+
88
+ `serve` always uses multi-agent layout by default: agents are mounted under
89
+ `/<slug>/v1/*` with an index at `/`. A directory that is itself an agent
90
+ project is mounted under its directory name. Opt into the unslugged
91
+ `/v1/*` surface with `serve(dir, { mode: "single" })`.
92
+
93
+ In multi-agent mode:
94
+
95
+ - `GET /` is a web index listing every agent, linking to its playground;
96
+ `GET /v1/agents` is the JSON equivalent.
97
+ - `GET /v1/health` is host-level liveness (for ALB/ECS); each agent also has
98
+ `/<slug>/v1/health`.
99
+ - each agent is fully namespaced: `/<slug>/v1/session`,
100
+ `/<slug>/v1/session/:id/stream`, `/<slug>/playground`, `/<slug>/v1/info`,
101
+ custom channels at `/<slug>/v1/channels/<id>`, etc.
102
+ - sessions are isolated per agent (`<stateRoot>/<slug>/`), and the playground
103
+ gains a "← all agents" link back to the index.
104
+ - point the terminal client at a slug:
105
+ `agent-serve chat --url http://127.0.0.1:3000/weather-agent`.
106
+
107
+ Slugs come from directory names and must match `[A-Za-z0-9][A-Za-z0-9_-]*`
108
+ and not collide with the reserved `v1` / `playground` / `docs` path
109
+ segments.
110
+
111
+ ## Agent-to-agent: every agent is an MCP server
112
+
113
+ Every mounted agent also serves the **Model Context Protocol** over
114
+ streamable HTTP at `/<slug>/v1/mcp` (or `/v1/mcp` in single mode), so other
115
+ agents — and any MCP client — can delegate work to it. The surface is
116
+ stateless (session identity travels in tool arguments) and runs the same
117
+ route auth chain as the session API. Tools:
118
+
119
+ | Tool | Behavior |
120
+ | ---- | -------- |
121
+ | `ask` | Send a message; runs a model turn in **this agent's own session, tools, and context** and returns `{ status, sessionId, reply }`. Omit `sessionId` for a fresh session; pass it to follow up. |
122
+ | `check` | Wait for / poll a running session (`waitSeconds: 0` for a non-blocking poll). |
123
+ | `call_tool` | Call one of the agent's deterministic server tools directly (no model turn). Registered only when the agent has server tools. |
124
+
125
+ Waits are bounded (~50s, below MCP client request timeouts): a long turn
126
+ returns `status: "running"` and the caller keeps waiting with `check`.
127
+ Sessions created this way live on the `mcp` channel, bind to the calling
128
+ principal, and show up in the playground and `GET /v1/sessions` like any
129
+ other session.
130
+
131
+ **Peer MCP connections** make delegation first-class between agents on the
132
+ same host. Author an MCP connection whose transport is a peer slug:
133
+
134
+ ```ts
135
+ // agents/concierge/agent/mcp-connections/weather.ts
136
+ import { defineConnection } from "@cursor/july/connections";
137
+
138
+ export default defineConnection({
139
+ agent: "weather-agent",
140
+ description: "Delegate weather questions to the weather agent.",
141
+ });
142
+ ```
143
+
144
+ The parent model then sees the peer's `ask` / `check` (/ `call_tool`) tools
145
+ under the `weather` server name — subagent-style delegation where the peer
146
+ keeps its **own** instructions, tools, MCP connections, and sessions. Peer URLs
147
+ resolve when the server starts (so `run` / `eval` ephemeral ports work):
148
+
149
+ - **Local-runtime turns** (and host-side `ctx.host.mcp` / channel handlers)
150
+ call the peer over loopback — works out of the box under the default
151
+ `localDevStrict()` auth.
152
+ - **Cloud-runtime turns** execute on a cloud VM that cannot reach this
153
+ host's loopback address. Pass `--public-url https://agent-serve.example.com`
154
+ (or `serve(dir, { publicUrl })`) so peers resolve to a reachable URL;
155
+ without it, peers are omitted from cloud turns (the server warns at
156
+ startup). With `--bearer-token`, the token is attached to peer calls
157
+ automatically so they pass the target agent's auth chain.
158
+
159
+ Unknown peer slugs and self-references fail at serve startup. Peers require
160
+ the multi-agent layout (each agent mounted under its slug). There is no
161
+ cross-host loop protection yet: if agent A's instructions delegate to B and
162
+ B's delegate back to A, they can recurse — scope each agent's delegation
163
+ instructions narrowly (the concierge above delegates *weather questions* to
164
+ `weather-agent`, not everything).
165
+
166
+ ```bash
167
+ agent-serve serve --dir ./agents --dev
168
+ agent-serve chat --url http://127.0.0.1:3000/concierge \
169
+ --message "What's the weather in Paris right now?"
170
+ # concierge → weather.ask → weather-agent's own session/tools → reply
171
+ ```
172
+
173
+ ## Quick start
174
+
175
+ A minimal agent is two files.
176
+
177
+ `agent/instructions.md`:
178
+
179
+ ```md
180
+ You are a concise assistant. Use tools when they are available.
181
+ ```
182
+
183
+ `agent/agent.ts`:
184
+
185
+ ```ts
186
+ import { defineAgent } from "@cursor/july";
187
+
188
+ export default defineAgent({
189
+ // optional — defaults to grok-4.5 with effort=high and fast=true
190
+ // runtime: "local", // default — Cursor SDK local harness
191
+ // runtime: "cloud",
192
+ // cloud: {
193
+ // repos: [{ url: "https://github.com/org/repo", startingRef: "main" }],
194
+ // },
195
+ });
196
+ ```
197
+
198
+ Serve it (turns run on the Cursor harness, so the host needs a Cursor
199
+ credential — sign in once, or export an API key):
200
+
201
+ ```bash
202
+ agent-serve login # browser sign-in; mints + stores a revocable API key
203
+ # or: export CURSOR_API_KEY=key_...
204
+ agent-serve serve --dir . --port 3000
205
+ # e.g. playground at http://127.0.0.1:3000/<dirname>/playground
206
+ ```
207
+
208
+ Start a session, then follow the NDJSON event stream (replace `<slug>` with
209
+ the agent directory name):
210
+
211
+ ```bash
212
+ curl -X POST http://127.0.0.1:3000/<slug>/v1/session \
213
+ -H 'content-type: application/json' \
214
+ -d '{"message":"What can you do?"}'
215
+ # {"ok":true,"sessionId":"ses_...","continuationToken":"http:..."}
216
+
217
+ curl -N http://127.0.0.1:3000/<slug>/v1/session/ses_.../stream
218
+ # {"type":"session.started",...}
219
+ # {"type":"message.appended","data":{"delta":"I can","text":"I can"},...}
220
+ # {"type":"message.completed",...}
221
+ # {"type":"turn.completed",...}
222
+ # {"type":"session.waiting",...}
223
+ ```
224
+
225
+ Send a follow-up to the same durable session with the continuation token
226
+ from the previous response (each accepted follow-up rotates it):
227
+
228
+ ```bash
229
+ curl -X POST http://127.0.0.1:3000/<slug>/v1/session/ses_... \
230
+ -H 'content-type: application/json' \
231
+ -d '{"continuationToken":"http:...","message":"Shorter, please."}'
232
+ ```
233
+
234
+ Inspect the discovered surface at any time:
235
+
236
+ ```bash
237
+ agent-serve info --dir . # or GET /<slug>/v1/info on a running server
238
+ agent-serve validate --dir . # exit non-zero on error diagnostics
239
+ ```
240
+
241
+ ## Terminal client
242
+
243
+ `agent-serve chat` talks to a running server over the same public API and
244
+ renders the reply live — streamed text, tool calls, and a per-turn usage
245
+ footer. Pass `--json` for a compact trajectory (same shape as `run`).
246
+
247
+ ```bash
248
+ # interactive REPL against a local server
249
+ agent-serve chat --url http://127.0.0.1:3000
250
+
251
+ # one-shot (good for piping / scripts)
252
+ agent-serve chat --url http://127.0.0.1:3000 --message "Weather in Paris?"
253
+
254
+ # JSON trajectory (for coding agents / scripts)
255
+ agent-serve chat --url http://127.0.0.1:3000 --message "hello" --json
256
+
257
+ # against an authed server
258
+ agent-serve chat --url https://my-agent.example.com --bearer-token "$TOKEN"
259
+ ```
260
+
261
+ ## Agent loop
262
+
263
+ Coding agents should prefer the JSON-first commands: edit files → inspect
264
+ → run a turn → assert with filesystem evals.
265
+
266
+ ```bash
267
+ agent-serve validate --dir .
268
+ agent-serve info --dir . --json
269
+
270
+ # Ephemeral server + one (or more) turns; JSON trajectory on stdout
271
+ agent-serve run --dir . --message "What's the temperature in NYC?"
272
+
273
+ # Optional event log for later summarization
274
+ agent-serve run --dir . --message "..." --events /tmp/run.ndjson
275
+ agent-serve trajectory --events /tmp/run.ndjson
276
+
277
+ # Scaffold (or print the setup guide with no directory)
278
+ agent-serve init ./my-agent
279
+ agent-serve init
280
+ ```
281
+
282
+ `run` / `eval` boot a temporary server on port `0` with a temp `stateRoot`
283
+ outside the project so session workspaces do not inherit ambient monorepo
284
+ `AGENTS.md` / `.cursor` rules. Pass `--url` to target an already-running
285
+ agent instead.
286
+
287
+ ### Evals
288
+
289
+ Author cases under `evals/**/*.eval.ts` with `defineEval`. Drive the
290
+ agent and assert inline:
291
+
292
+ ```ts
293
+ import { defineEval, includes } from "@cursor/july/evals";
294
+
295
+ export default defineEval({
296
+ description: "Uses get_weather for temperature questions.",
297
+ tags: ["smoke"],
298
+ async test(t) {
299
+ await t.send("What's the temperature in NYC?");
300
+ t.succeeded();
301
+ t.calledTool("get_weather");
302
+ t.check(t.reply, includes(/°|[FC]/));
303
+ },
304
+ });
305
+ ```
306
+
307
+ Case id is the path under `evals/` for a single-`test` file, or
308
+ `<fileId>/<case.id>` when using `cases` in one file.
309
+
310
+ Every project with evals needs `evals/evals.config.ts` with a required
311
+ `maxConcurrency` (hard-capped at 200 due to model provider request limits).
312
+ Playground batches are in-memory by default; set optional `persistRuns` to
313
+ keep them across serve restarts. Optional `maxPlaygroundRuns` caps how many
314
+ batches the playground / `/v1/dev/evals*` history keeps (default 20):
315
+
316
+ ```ts
317
+ import {
318
+ defineEvalConfig,
319
+ persistEvalRunsToDir,
320
+ } from "@cursor/july/evals";
321
+
322
+ export default defineEvalConfig({
323
+ maxConcurrency: 20,
324
+ // Optional — playground /v1/dev/evals history window (default 20):
325
+ // maxPlaygroundRuns: 50,
326
+ // Optional — survive serve restarts (omit to keep runs in memory only):
327
+ persistRuns: persistEvalRunsToDir(".agent-serve/eval-runs"),
328
+ });
329
+ ```
330
+
331
+ ```bash
332
+ agent-serve eval --dir . --list
333
+ agent-serve eval --dir . --json
334
+ agent-serve eval --dir . weather/nyc # one datapoint
335
+ agent-serve eval --dir . weather # whole file
336
+ agent-serve eval --dir . weather forecast # multiple files
337
+ agent-serve eval --dir . --tag smoke
338
+ ```
339
+
340
+ See [`docs/`](./docs/README.md) for the full documentation set,
341
+ [`AGENTS.md`](./AGENTS.md) for the coding-agent guide, and
342
+ [`skills/`](./skills/) for task-shaped guides (create an agent, author
343
+ evals, hillclimb, GitHub webhooks, Slack setup, debugging).
344
+
345
+ ## Web playground
346
+
347
+ Every served agent ships with a built-in playground at
348
+ `http://127.0.0.1:3000/playground` — a Vite + React SPA (reusing
349
+ Cursor's internal UI components) over the same public HTTP API, made for quick
350
+ manual testing and demo recordings:
351
+
352
+ - chat with the agent and watch text/reasoning stream live, rendered as
353
+ markdown (headings, lists, tables, blockquotes, links) with syntax
354
+ highlighting for fenced code blocks (C-like languages, Python, shell,
355
+ and diffs),
356
+ - invoke custom channels as **slash commands** in the composer (e.g. a
357
+ `/drive https://github.com/org/repo/pull/1` channel route) — routes from `GET /v1/info`,
358
+ with `/help` and autocomplete; same HTTP as the Agent surface **Try**
359
+ buttons,
360
+ - see tool calls inline (args, output, error state) as `actions.requested`
361
+ / `action.result` events arrive,
362
+ - browse every session (chat, custom-channel, and schedule task sessions)
363
+ and replay their durable event streams,
364
+ - dispatch schedules by hand in dev mode,
365
+ - inspect the discovered agent surface (tools, skills, subagents, MCP
366
+ connections, channels, hooks),
367
+ - flip on the raw NDJSON pane to see the exact wire events.
368
+
369
+ The SPA is a static bundle. `agent-serve serve` auto-builds `dist/playground/`
370
+ when it is missing and the local vite toolchain is present (`pnpm run build`
371
+ also emits it for publish). The server serves the bundle and every call it
372
+ makes runs the normal route auth chain (there's a bearer-token field for
373
+ non-loopback setups). Disable it with `--no-playground` (CLI) or
374
+ `serve(dir, { playground: false })`. When serving many agents, each has its
375
+ own playground at `/<slug>/playground` and `/` is an index of them all (see
376
+ "Serving many agents at once").
377
+
378
+ ### Developing the playground
379
+
380
+ `serve --dev` also starts Vite HMR (default `:5273`) and prints
381
+ `playground (HMR): …`. Single-agent proxies `/v1` to the serve URL; multi-agent
382
+ serves the agents index at `/` and each SPA at `/<slug>/playground`:
383
+
384
+ ```bash
385
+ # single agent — auto-build + HMR in one process
386
+ pnpm exec tsx src/bin/agent-serve.ts serve --dir ./my-agent --dev
387
+
388
+ # monorepo dev: multi-agent HMR
389
+ mise //packages/agent-serve:start
390
+ # → backend :3000, playground HMR :5273 (open /, then /<slug>/playground)
391
+
392
+ # pin one slug at the HMR root, or UI-only against an already-running serve
393
+ AGENT_SERVE_BASE=/my-agent mise //packages/agent-serve:start
394
+ AGENT_SERVE_MULTI=1 mise //packages/agent-serve:dev-playground
395
+ ```
396
+
397
+ The Vite dev server proxies API calls to the backend (`AGENT_SERVE_TARGET`,
398
+ default `http://127.0.0.1:3000`). Multi-agent HMR proxies `/<slug>/v1/*` as-is
399
+ and serves each SPA at `/<slug>/playground`; a single-slug pin uses
400
+ `AGENT_SERVE_BASE=/<slug>` at the HMR root. Editing anything under
401
+ `playground/src` hot-reloads in the browser. The playground source lives in
402
+ `playground/` (entry `playground/src/main.tsx`); markdown rendering and the
403
+ trace model are plain modules under `playground/src/lib` with unit tests.
404
+
405
+ To share the server beyond localhost (a tunnel, a LAN address, a phone),
406
+ pass `--bearer-token <secret>` (or `serve(dir, { authToken })`). That
407
+ replaces the loopback-only default with `bearerAuth(secret)` on every
408
+ channel that doesn't author its own auth. The default
409
+ `localDevStrict()` only admits direct loopback callers, rejects
410
+ proxy-forwarding headers (`X-Forwarded-For`, `X-Real-IP`, `Forwarded`,
411
+ `X-Forwarded-Host`), and requires a loopback `Host` header, so
412
+ same-host reverse proxies / tunnels do not accidentally re-expose the
413
+ default routes. Open the playground on the remote device and paste the
414
+ token into the top-right field.
415
+
416
+ Session follow-up, stream, and list routes also bind to the principal that
417
+ created the session (`403` when a different admitted principal addresses
418
+ someone else's handle). Session ids are restricted to a single safe path
419
+ segment before they touch `<stateRoot>/sessions`.
420
+
421
+ ## How it runs on the Cursor harness
422
+
423
+ Every session is one Cursor SDK agent (`Agent.create` / `Agent.resume`).
424
+ Local agents use the session id as the SDK agent id; cloud agents persist
425
+ a separate `sdkAgentId` (typically `bc-…`). Each session gets its own
426
+ workspace directory, materialized from the authored files and handed to
427
+ the local harness as its working directory:
428
+
429
+ | Folder or file | Runtime mapping |
430
+ | ------------------------ | ------------------------------------------------------------------------------- |
431
+ | `instructions.*` | Local: `AGENTS.md` in the session workspace. Cloud: prepended to the first prompt. |
432
+ | `tools/*.ts` (`execution: "server"`, default) | Local only — in-process SDK custom tools. Not available on cloud. |
433
+ | `tools/*.ts` (`execution: "agent"`) | Local: scripts under `.agent-serve/tools/` + catalog in `AGENTS.md`. Cloud: catalog + script bodies on the first prompt. |
434
+ | `skills/*` | Local: `.cursor/skills/<name>/SKILL.md` in the workspace. Cloud: only if present in the cloud repo. |
435
+ | `mcp-connections/*.ts` | Always three places: Cursor agent via SDK `mcpServers` (local + cloud), host-side `ctx.host.mcp` for in-process tools, and `args.host.mcp` on channel/schedule handlers. |
436
+ | `subagents/<id>/` | SDK custom subagents (the model delegates via the harness `task` tool) |
437
+ | `sandbox/workspace/**` | Local session workspace seed on first turn; ignored for cloud runtime. |
438
+ | `channels/`, `schedules/`, `hooks/` | Served by this framework around the harness |
439
+
440
+ Conversation state for local agents persists through the SDK's local store
441
+ under `.agent-serve/runner/`, and every session's event stream is recorded
442
+ to `.agent-serve/sessions/<id>/events.ndjson` — sessions survive server
443
+ restarts, and streams replay from any `startIndex`.
444
+
445
+ ## Folder structure
446
+
447
+ ### `agent.ts`
448
+
449
+ ```ts
450
+ import { defineAgent } from "@cursor/july";
451
+
452
+ export default defineAgent({
453
+ model: {
454
+ id: "grok-4.5",
455
+ params: [
456
+ { id: "effort", value: "high" },
457
+ { id: "fast", value: "true" },
458
+ ],
459
+ }, // optional; this is the default
460
+ runtime: "local", // default — or "cloud"
461
+ // cloud: {
462
+ // repos: [{ url: "https://github.com/org/repo", startingRef: "main" }],
463
+ // env: { type: "cloud" },
464
+ // },
465
+ });
466
+ ```
467
+
468
+ `model` accepts a Cursor model id string or `{ id, params }`. When omitted
469
+ on the root agent, agent-serve defaults to `grok-4.5` with `effort=high`
470
+ and `fast=true`.
471
+
472
+ `runtime` selects where turns execute:
473
+
474
+ | Value | Behavior |
475
+ | --------- | ------- |
476
+ | `"local"` | Cursor SDK local harness on this machine. Session id doubles as the SDK agent id. Authored tools, skills, and sandbox seeds apply. |
477
+ | `"cloud"` | Cursor cloud agents. Pass a `cloud` block (repos, env, envVars, …) forwarded to the SDK. In-process server tools are not available; instructions and agent-tool catalogs are prepended to the first prompt because the local session workspace is not the cloud VM. |
478
+
479
+ Discovery warns when `runtime: "cloud"` is combined with server tools, skills, or sandbox seeds that only apply locally.
480
+
481
+ ### Instructions
482
+
483
+ `agent/instructions.md` is the always-on system prompt (required on the
484
+ root agent). When the prompt needs to be generated, use
485
+ `agent/instructions.ts` with `defineInstructions({ markdown })` or a plain
486
+ string default export, or split prose across an `agent/instructions/`
487
+ directory (composed in filename order).
488
+
489
+ ### Tools (`agent/tools/*.ts`)
490
+
491
+ One file per tool; the filename is the tool name the model sees.
492
+
493
+ `execution` chooses where the tool body runs:
494
+
495
+ | Value | Behavior |
496
+ | ----- | -------- |
497
+ | `"server"` (default) | In-process on the agent-serve host via Cursor SDK custom tools. Requires `execute`. Only available when the agent `runtime` is `"local"`. |
498
+ | `"agent"` | Materialized into the agent environment as a shell script. Requires `script` (JSON on stdin, result on stdout). Works with local and cloud agent runtimes. |
499
+
500
+ Server tool (default):
501
+
502
+ ```ts
503
+ import { defineTool } from "@cursor/july/tools";
504
+ import { z } from "zod";
505
+
506
+ export default defineTool({
507
+ description: "Get the current weather for a city.",
508
+ // execution: "server", // default
509
+ inputSchema: z.object({ city: z.string() }),
510
+ async execute({ city }, ctx) {
511
+ return { city, condition: "Sunny", temperatureF: 72 };
512
+ },
513
+ });
514
+ ```
515
+
516
+ Human-in-the-loop: set `needsApproval: true` (or an
517
+ `(input) => boolean` predicate on validated args) so the host parks the in-flight
518
+ tool call until a human approves or denies. The turn stays running; the
519
+ stream emits `action.approval_requested` / `action.approval_resolved`.
520
+
521
+ **Resolve surfaces** (park is always on when `needsApproval` is set):
522
+
523
+ - Playground Approve / Deny buttons
524
+ - HTTP: `POST /v1/session/:sessionId/approvals/:callId` with
525
+ `{"decision":"approve"|"deny"}`
526
+ - **Slack** (opt-in channel surface) — set `toolApprovals: true` on
527
+ `slackChannel`, and enable `interactivity` in the Slack app
528
+ manifest:
529
+
530
+ ```ts
531
+ import { slackChannel } from "@cursor/july/channels/slack";
532
+
533
+ export default slackChannel({
534
+ credentials: { /* … */ },
535
+ toolApprovals: true, // posts Block Kit Approve/Deny + routes clicks
536
+ });
537
+ ```
538
+
539
+ Or compose the builders yourself:
540
+
541
+ ```typescript
542
+ import {
543
+ buildDefaultEvents,
544
+ buildToolApprovalEvents,
545
+ slackChannel,
546
+ } from "@cursor/july/channels/slack";
547
+
548
+ export default slackChannel({
549
+ deliverDefaults: false,
550
+ // Required so Socket Mode routes Approve/Deny clicks when composing
551
+ // buildToolApprovalEvents by hand (toolApprovals: true also enables this).
552
+ interactivity: true,
553
+ events: {
554
+ ...buildDefaultEvents({ credentials }),
555
+ ...buildToolApprovalEvents({ credentials }),
556
+ },
557
+ });
558
+ ```
559
+
560
+ In `--dev`, the playground can list and stream Slack sessions, and Approve/Deny
561
+ parked tools on those sessions (audit `by` records the HTTP caller). That bridge
562
+ does **not** open cross-owner approval for HTTP sessions. Production and
563
+ bearer-auth hosts stay strict: Slack resolve must come from Slack interactivity
564
+ (or a matching principal).
565
+
566
+ `--allow-anonymous` is for trusted-network demos only — every HTTP caller shares
567
+ the same `anonymous` principal. Prefer `--bearer-token` when the host is shared.
568
+
569
+ Slack cards show **redacted / truncated** args for Block Kit limits; execution
570
+ still uses the full validated tool input. Review sensitive tools in the
571
+ playground or a private surface when args may exceed the card.
572
+
573
+ Example tool:
574
+
575
+ ```ts
576
+ export default defineTool({
577
+ description: "Post a weather alert to ops.",
578
+ needsApproval: true,
579
+ inputSchema: z.object({
580
+ city: z.string(),
581
+ message: z.string(),
582
+ }),
583
+ async execute({ city, message }) {
584
+ return { posted: true, city, message };
585
+ },
586
+ });
587
+ ```
588
+
589
+ Approvals are only supported for `execution: "server"` tools on the
590
+ `local` runtime. Exact resume of a parked SDK tool call does **not**
591
+ survive host process restart — pending approvals left after a crash are
592
+ treated as interrupted.
593
+ Agent tool (runs where the Cursor agent runs — local harness or cloud VM):
594
+
595
+ ```ts
596
+ import { defineTool } from "@cursor/july/tools";
597
+ import { z } from "zod";
598
+
599
+ export default defineTool({
600
+ description: "Echo a message from the agent workspace.",
601
+ execution: "agent",
602
+ inputSchema: z.object({ message: z.string() }),
603
+ script: `#!/usr/bin/env bash
604
+ set -euo pipefail
605
+ message=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["message"])')
606
+ printf '%s\\n' "$message"
607
+ `,
608
+ });
609
+ ```
610
+
611
+ `inputSchema` is a zod schema (validated and typed for server tools) or a
612
+ plain JSON Schema object (forwarded as-is). For server tools, `ctx` carries
613
+ `{ toolCallId, session, workspaceDir }`. Return a string, a JSON value, or
614
+ `{ content: [...], isError? }` for rich results.
615
+
616
+ #### Deterministic tool calls
617
+
618
+ Server tools can also be called **deterministically** — you pick the tool
619
+ and the input, no model turn decides anything. The input is validated
620
+ against the tool's schema and `execute` runs in-process; the result comes
621
+ back exactly as the model would receive it. No Cursor API key is needed.
622
+
623
+ Over HTTP (`POST /v1/tools/:toolName`, same auth chain as the session API):
624
+
625
+ ```bash
626
+ curl -X POST http://127.0.0.1:3000/<slug>/v1/tools/get_weather \
627
+ -H 'content-type: application/json' \
628
+ -d '{"input":{"city":"NYC"}}'
629
+ # {"ok":true,"toolName":"get_weather","callId":"tool_get_weather_...",
630
+ # "isError":false,"result":{...},"durationMs":12}
631
+ ```
632
+
633
+ From the CLI (boots an ephemeral server unless `--url` targets a running
634
+ one):
635
+
636
+ ```bash
637
+ agent-serve call get_weather --dir . --input '{"city":"NYC"}'
638
+ agent-serve call get_weather --url http://127.0.0.1:3000/<slug> --input '{"city":"NYC"}'
639
+ ```
640
+
641
+ Programmatically, `callTool(toolName, input, options?)` is available on the
642
+ serve handle, on channel route handlers and `onStart` args, and on schedule
643
+ `run` handlers — so a channel can mix deterministic tool calls with model
644
+ turns (e.g. fetch PR metadata deterministically, then `send()` the review
645
+ prompt):
646
+
647
+ ```ts
648
+ const outcome = await handle.callTool("get_weather", { city: "NYC" });
649
+ // { toolName, callId, isError, result, durationMs }
650
+ ```
651
+
652
+ By default the tool runs against an **ephemeral** scratch workspace under
653
+ `<stateRoot>/tool-calls/<callId>` with a synthetic `direct` session context —
654
+ materialized like a session workspace (AGENTS.md, skills, seed files) and
655
+ removed once the call returns. Pass `sessionId` (body field over HTTP,
656
+ `--session` on the CLI, `options.sessionId` programmatically) to run it
657
+ **inside an existing session** instead: the tool sees that session's info
658
+ and materialized workspace, and the call is recorded on the session's event
659
+ stream as `actions.requested` / `action.result` under a per-call `turnId` —
660
+ visible in the playground, NDJSON trace, and trajectories like any
661
+ model-initiated call. Session-bound calls are serialized with model turns:
662
+ while a turn is running the call is rejected with `409 session_busy`.
663
+
664
+ Unknown tools are rejected with the available tool names, `execution:
665
+ "agent"` tools cannot be called on the host (400), schema-invalid input is a
666
+ 400 before the tool body runs (zod schemas validate; plain JSON Schema
667
+ inputs pass through unvalidated, matching the model path), and a tool body
668
+ that throws is reported as `isError: true` with the same
669
+ `{ content, isError }` envelope the model would see.
670
+
671
+ ### Skills (`agent/skills/*`)
672
+
673
+ Skills follow the `SKILL.md` convention: model-loadable procedures the
674
+ harness advertises by description and loads on demand. Author them as flat
675
+ markdown (`skills/forecast.md`, optional `description` frontmatter — the
676
+ first body line is the fallback), packaged directories
677
+ (`skills/research/SKILL.md` plus `references/…`, which require `description`
678
+ frontmatter), or TypeScript (`defineSkill` from
679
+ `@cursor/july/skills`) when content must be generated.
680
+
681
+ ### MCP connections (`agent/mcp-connections/*.ts`)
682
+
683
+ ```ts
684
+ import { defineConnection } from "@cursor/july/connections";
685
+
686
+ export default defineConnection({
687
+ url: "https://mcp.linear.app/mcp",
688
+ headers: { authorization: `Bearer ${process.env.LINEAR_TOKEN}` },
689
+ });
690
+ ```
691
+
692
+ The filename becomes the MCP server name. `{ command, args, env, cwd }`
693
+ declares a local stdio server instead, and `{ agent: "<slug>" }` declares a
694
+ **peer MCP connection** to another agent mounted on the same serve host (see
695
+ "Agent-to-agent: every agent is an MCP server").
696
+
697
+ `{ cursorAccount: true }` declares a **Cursor account MCP connection**: the
698
+ agent gets the MCP connectors the signed-in Cursor account already
699
+ authorized (dashboard → MCP) with zero token plumbing. Every tool executes
700
+ on the Cursor backend with the account's stored OAuth credentials — raw
701
+ tokens never reach the serve host, session workspaces, or traces. Optional
702
+ `servers: ["Linear", …]` restricts which connectors are exposed.
703
+
704
+ ```ts
705
+ // agent/mcp-connections/cursor.ts — the account's connectors, one line
706
+ export default defineConnection({ cursorAccount: true, servers: ["Linear"] });
707
+ ```
708
+
709
+ Requirements and behavior:
710
+
711
+ - The host must be signed in (`agent-serve login` or `CURSOR_API_KEY`);
712
+ `serve` fails fast at startup otherwise, and logs each connector's live
713
+ status (`connected` / `needsAuth` / `error`) as it starts.
714
+ - Local turns and host-side calls go through a loopback bridge route guarded
715
+ by a per-boot secret. Cloud-runtime turns reach the same bridge through the
716
+ serve `--public-url` (so `servers` filters apply there too); without one,
717
+ cloud turns fall back to the account's natively hydrated connectors
718
+ (unfiltered) and the serve host logs why.
719
+ - Backend execution covers the account's HTTP/SSE servers; stdio servers
720
+ cannot run server-side (author a `{ command }` MCP connection for those).
721
+ - **Exposure**: whoever can talk to the agent can drive these connectors
722
+ (they are ordinary agent tools). `serve` refuses to start when
723
+ `--allow-anonymous` is combined with Cursor account MCP connections; use
724
+ `--bearer-token` on shared hosts.
725
+
726
+ Every MCP connection is always available in three places:
727
+
728
+ 1. the Cursor agent (local or cloud), via SDK `mcpServers`
729
+ 2. the agent-serve host, for in-process tools via `ctx.host.mcp`
730
+ 3. channel / schedule handlers, via `args.host.mcp` (deterministic — no agent loop)
731
+
732
+ ```ts
733
+ export default defineTool({
734
+ description: "Search Linear issues.",
735
+ inputSchema: z.object({ query: z.string() }),
736
+ async execute({ query }, ctx) {
737
+ return ctx.host.mcp.callTool("linear", "list_issues", { query });
738
+ },
739
+ });
740
+ ```
741
+
742
+ ```ts
743
+ // agent/channels/webhook.ts — call MCP directly from a webhook
744
+ POST("/sync", {
745
+ bodySchema: z.object({}),
746
+ handler: async (_req, { host }) => {
747
+ const result = await host.mcp.callTool("linear", "list_issues", {});
748
+ return Response.json(result);
749
+ },
750
+ });
751
+ ```
752
+
753
+ `host.mcp.names()` lists MCP connection names; `listTools(name)` and
754
+ `callTool(name, tool, args)` open the client lazily on first use.
755
+
756
+ ### Subagents (`agent/subagents/<id>/`)
757
+
758
+ A subagent is its own directory with the same `agent.ts` +
759
+ `instructions.md` shape. `description` is required — the parent model reads
760
+ it to decide when to delegate — and `model` is optional (`inherit` by
761
+ default). On the Cursor harness, subagents run as SDK custom subagents:
762
+ they inherit the parent's execution surface, so per-subagent `tools/`,
763
+ `skills/`, and `mcp-connections/` are reported as warnings and ignored for now.
764
+
765
+ ### Channels (`agent/channels/*.ts`)
766
+
767
+ The **built-in HTTP channel** is always mounted (under `/<slug>` in the
768
+ default multi-agent layout; at the server root with `mode: "single"`):
769
+
770
+ - `POST /v1/session` — start a session (`{"message": "..."}`; returns
771
+ `sessionId` + `continuationToken`)
772
+ - `POST /v1/session/:sessionId` — follow-up (`{"message", "continuationToken"}`;
773
+ rotates the token; works for any chat session including custom channels like
774
+ `drive`; `409` on stale tokens, busy sessions, or task/schedule sessions;
775
+ `403` if the caller is not the session owner)
776
+ - `GET /v1/session/:sessionId/stream?startIndex=N` — replay + live NDJSON
777
+ (same owner check)
778
+ - `GET /v1/session/:sessionId/approvals` — pending human-in-the-loop
779
+ tool approvals for the session
780
+ - `POST /v1/session/:sessionId/approvals/:callId` — approve or deny
781
+ (`{"decision":"approve"|"deny"}`)
782
+ - `GET /v1/sessions` — sessions owned by the calling principal
783
+ - `POST /v1/tools/:toolName` — call a server tool deterministically
784
+ (`{"input": {...}, "sessionId"?}`; see "Deterministic tool calls")
785
+ - `GET /v1/health`, `GET /v1/info` — liveness and the manifest snapshot
786
+ Author `agent/channels/http.ts` only to override its defaults:
787
+
788
+ ```ts
789
+ import {
790
+ bearerAuth,
791
+ httpChannel,
792
+ localDevStrict,
793
+ } from "@cursor/july/channels";
794
+
795
+ export default httpChannel({
796
+ auth: [localDevStrict(), bearerAuth(process.env.AGENT_TOKEN ?? "")],
797
+ onMessage: (message, { auth }) =>
798
+ `[caller ${auth?.principalId ?? "anonymous"}] ${message}`,
799
+ });
800
+ ```
801
+
802
+ **Custom channels** declare their own routes (mounted under
803
+ `/v1/channels/<id>`), observe stream events for the sessions they own, and
804
+ control their continuation-token format (e.g. a thread id):
805
+
806
+ ```ts
807
+ import { defineChannel, POST } from "@cursor/july/channels";
808
+ import { z } from "zod";
809
+
810
+ export default defineChannel({
811
+ routes: [
812
+ POST("/message", {
813
+ description: "Enqueue a chat turn on this channel",
814
+ bodySchema: z.object({
815
+ message: z.string(),
816
+ thread: z.string().optional(),
817
+ }),
818
+ handler: async (_req, { send, body }) => {
819
+ const { message, thread } = body;
820
+ // `auth` defaults to the request principal resolved by the auth chain.
821
+ const session = await send(message, {
822
+ continuationToken: thread, // stable key: same thread, same session
823
+ });
824
+ return Response.json({ sessionId: session.id });
825
+ },
826
+ }),
827
+ ],
828
+ events: {
829
+ "message.completed"(event, channel, ctx) {
830
+ // deliver the reply back to the surface that owns this channel
831
+ },
832
+ },
833
+ });
834
+ ```
835
+
836
+ `GET` requires a Zod `querySchema` and `POST` / `PUT` / `PATCH` require a
837
+ Zod `bodySchema` at compile time — plain JSON Schema objects will not
838
+ type-check. Use `z.object({})` or `z.unknown()` when the surface is
839
+ intentionally open. Schemas are validated by the host before the handler
840
+ runs (handlers get typed `args.body` / `args.query`) and projected on
841
+ `GET /v1/info` so the playground Agent surface can **Try** the route, and
842
+ so the composer can offer matching **slash commands** (e.g. `/drive`).
843
+
844
+ Route handlers receive a Fetch `Request` and helpers: `send`, `getSession`,
845
+ `receive` (cross-channel hand-off), `params`, `requestIp`, `auth`, `host`
846
+ (shared host services — MCP / GitHub / Slack; same as tool `ctx.host`), and
847
+ `waitUntil`. Channel `state` declares initial per-session adapter state,
848
+ persisted across events; handlers receive it on `channel.state`.
849
+
850
+ **Auth**: every route runs an auth-policy chain (`auth` on the channel).
851
+ The default is `[localDevStrict()]` — direct loopback callers only, with
852
+ proxy-forwarding headers and non-loopback `Host` rejected — so nothing
853
+ is exposed publicly until you add real auth (`bearerAuth(...)`, a custom
854
+ policy, or the explicit `allowAll()`).
855
+
856
+ **Slack** (`@cursor/july/channels/slack`): a platform channel pack
857
+ that defaults to **Socket Mode**. Author `agent/channels/slack.ts` with
858
+ `slackChannel()`:
859
+
860
+ ```ts
861
+ import { slackChannel } from "@cursor/july/channels/slack";
862
+
863
+ // Single agent — SLACK_BOT_TOKEN + SLACK_APP_TOKEN
864
+ export default slackChannel();
865
+
866
+ // Multi-agent serve — one Slack app (and token pair) per agent
867
+ export default slackChannel({ envPrefix: "WEATHER_AGENT" });
868
+ // → WEATHER_AGENT_SLACK_BOT_TOKEN + WEATHER_AGENT_SLACK_APP_TOKEN
869
+
870
+ // Cursor account connection — no dedicated Slack app. `@Cursor Weatherbot …`
871
+ // routes here; replies post as "Weatherbot" through the Cursor Slack app.
872
+ export default slackChannel({ cursorAccount: true, agentName: "Weatherbot" });
873
+ ```
874
+
875
+ Transport uses `@slack/socket-mode` + `@slack/web-api`. Socket Mode starts
876
+ on channel mount when both tokens are present; otherwise the channel stays
877
+ **idle** (`channel idle … missing credentials`) so multi-agent serve can
878
+ mount agents that do not have Slack tokens configured yet.
879
+
880
+ The pack dispatches via `waitUntil`, streams assistant text (`chat.startStream` /
881
+ `appendStream` / `stopStream`) with postMessage fallback, shows rotating status
882
+ + tool thinking steps, and sets thread titles / suggested prompts. Handlers may
883
+ return a prepared `message` / `workspaceFiles` / `cloud` to host-prepare PR
884
+ reviews or attach
885
+ cloud repos from an `@mention`.
886
+
887
+ **Engagement:** by default the agent is summoned, never proactive — it
888
+ dispatches only on `app_mention` and DMs. Channel watch is an explicit opt-in:
889
+
890
+ ```ts
891
+ export default slackChannel({
892
+ envPrefix: "TRIAGE",
893
+ engagement: {
894
+ // mentions / directMessages default to true
895
+ channelPosts: {
896
+ allow: ["#triage-alerts"], // explicit allowlist only; no wildcard
897
+ posts: "top-level", // default: never dispatch on thread replies
898
+ debounceMs: 15_000, // optional: let rapid edits settle
899
+ },
900
+ },
901
+ onChannelPost: async (ctx, message) => {
902
+ // Same contract as onAppMention: return null to skip.
903
+ return message.markdown.length > 20 ? {} : null;
904
+ },
905
+ });
906
+ ```
907
+
908
+ Watched posts dispatch with the same thread-scoped principal as mentions
909
+ (so a later `@mention` continues the session) plus an
910
+ `engagement: "channel_post"` auth attribute. Posts that mention the bot are
911
+ left to the `app_mention` path; bot-authored posts never dispatch; deleting
912
+ a post inside the debounce window cancels its dispatch. The Slack app must
913
+ subscribe to `message.channels` / `message.groups`
914
+ (`agent-serve slack init --channel-posts`, or add the events to an existing
915
+ app) and be a member of each watched channel. Watched channels surface in
916
+ `info` via `meta.slackChannelPosts`.
917
+
918
+ **HITL compose:** park/resume is independent (`needsApproval` on tools). Opt in
919
+ to Slack delivery with `toolApprovals: true` (or spread
920
+ `buildToolApprovalEvents` into `events` and set `interactivity: true` on
921
+ `slackChannel` so Approve/Deny buttons resolve). That posts Block Kit
922
+ Approve/Deny cards and routes Socket Mode `interactive` clicks to
923
+ `resolveApproval`. Enable `interactivity: true` on
924
+ `buildSlackManifest` when you use that surface. Threads bind with
925
+ `continuationToken = channelId:threadTs`. Dev and prod are
926
+ **separate Slack apps**. Requires `SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN`
927
+ (App-Level Token with `connections:write`).
928
+
929
+ Most demos under `examples/` use the Cursor Slack connection
930
+ (`cursorAccount: true`) with a unique `agentName` — `@Cursor Benny …`,
931
+ `@Cursor Bugbot …`, etc. Sign the host in, then mention the agent; no
932
+ per-agent Slack app required for chat. Agents that need channel watch or
933
+ tool approvals keep a Socket Mode app in a second channel file (e.g.
934
+ `slack-app.ts`) with their own env prefix (`<PREFIX>_SLACK_BOT_TOKEN` +
935
+ `<PREFIX>_SLACK_APP_TOKEN`, derived from the directory name by `slack init`).
936
+
937
+ | Agent | `@Cursor` name | Optional Socket Mode app |
938
+ | --- | --- | --- |
939
+ | `weather-agent` | Weather | `slack-app.ts` (`WEATHER_AGENT_SLACK_*`, tool approvals) |
940
+ | `slack-agent` | Slack Agent | — |
941
+ | `bugbot` | Bugbot | — |
942
+ | `fsd` | FSD | — |
943
+
944
+ PR-oriented demos (`bugbot`, `fsd`) extract a GitHub PR URL / `owner/repo#N`
945
+ from the mention and run the same host path as their HTTP channels.
946
+ `security-reviewer` is GitHub-task-only (no Slack channel); `concierge` is
947
+ agent-to-agent only (no Slack channel).
948
+
949
+ For Socket Mode apps (channel watch / approvals): generate manifests →
950
+ create apps → install + mint app token → env →
951
+ `agent-serve slack doctor --prefix …` → `serve`.
952
+
953
+ CLI (`agent-serve slack …`; guided setup in `skills/setup-slack/SKILL.md`):
954
+
955
+ ```bash
956
+ agent-serve slack setup
957
+ agent-serve slack init --dir ./my-agent --name "My Agent"
958
+ agent-serve slack doctor --prefix MY_AGENT
959
+ ```
960
+
961
+ `init` / `manifest` write `.agent-serve/slack/manifest.{dev,prod}.json` and
962
+ `env.example`.
963
+
964
+ **GitHub** (`@cursor/july/channels/github`): a webhook channel pack
965
+ (Eve-compatible dispatch). Author `agent/channels/github.ts` with
966
+ `githubChannel()` and declare inbound hooks:
967
+
968
+ ```ts
969
+ import { defaultGitHubAuth, githubChannel } from "@cursor/july/channels/github";
970
+
971
+ export default githubChannel({
972
+ botName: "my-agent", // or GITHUB_APP_SLUG
973
+ credentials: { webhookSecret: () => process.env.GITHUB_WEBHOOK_SECRET },
974
+ onPullRequest: (ctx, pr) =>
975
+ pr.action === "opened" ? { auth: defaultGitHubAuth(ctx) } : null,
976
+ onCheckSuite: (ctx, suite) =>
977
+ suite.conclusion === "failure" ? { task: () => triage(ctx) } : null,
978
+ });
979
+ ```
980
+
981
+ Mounts at `POST /<slug>/v1/channels/github`. When a webhook secret is set the
982
+ channel verifies `X-Hub-Signature-256` before parsing (the HMAC becomes the
983
+ request auth); without one it stays loopback-only (`localDevStrict()`) — except
984
+ under `serve --dev`, which admits unsigned loopback deliveries so
985
+ `gh webhook forward` and fixtures work with zero config. Hooks return
986
+ `{ auth }` to start a model turn as the actor, `{ task }` for host-side work
987
+ (202 ACK, runs past GitHub's ~10s timeout), or `null` to skip.
988
+
989
+ **Testing GitHub agents locally.** API auth prefers GitHub App installation
990
+ tokens when `GITHUB_APP_ID` / `GITHUB_APP_PRIVATE_KEY` (and an installation id)
991
+ are set; for local testing you can omit those and use `GITHUB_TOKEN` /
992
+ `GH_TOKEN` or `gh auth login` instead. The channel publishes the webhook events
993
+ it dispatches on (derived from the declared hooks, or pinned via
994
+ `webhookEvents`), so `agent-serve github …` can forward live deliveries with
995
+ zero hand-listing — it wraps [`gh webhook
996
+ forward`](https://docs.github.com/en/webhooks/testing-and-troubleshooting-webhooks/using-the-github-cli-to-forward-webhooks-for-testing):
997
+
998
+ ```bash
999
+ # One-time: gh + the cli/gh-webhook extension
1000
+ agent-serve github doctor --install
1001
+
1002
+ # Terminal A: serve the agent (dev accepts unsigned loopback deliveries)
1003
+ agent-serve serve --dir ./my-agent --dev
1004
+
1005
+ # Terminal B: forward this repo's deliveries to the discovered github channel
1006
+ # (URL + events auto-derived; repo inferred from the git remote). No secret
1007
+ # needed against a --dev server; set GITHUB_WEBHOOK_SECRET to verify signatures.
1008
+ agent-serve github forward --dir ./my-agent
1009
+
1010
+ # Inspect what would be forwarded (URL + events per agent)
1011
+ agent-serve github events --dir ./agents --json
1012
+
1013
+ # Forward to EVERY discovered github channel at once
1014
+ agent-serve github forward --dir ./agents
1015
+ ```
1016
+
1017
+ When several channels match (e.g. a folder of agent projects with more than
1018
+ one github channel), one forwarder fans out to all of them: `gh webhook
1019
+ forward` runs against a local proxy that re-posts each raw (still-signed)
1020
+ delivery to the channels whose event set matches. This is required — GitHub
1021
+ allows only one forwarder per repo, and `gh webhook forward` targets a single
1022
+ URL, so N processes would collide with `Hook already exists`.
1023
+
1024
+ `gh webhook forward` needs **admin** on the repo (or org owner for `--org`) —
1025
+ it registers a real webhook — and authenticates its relay with the GitHub CLI's
1026
+ own login. If `GITHUB_TOKEN` / `GH_TOKEN` is set in your env, deliveries fail
1027
+ with **HTTP 401** (the relay rejects env tokens); blank it for the command
1028
+ (`GITHUB_TOKEN= GH_TOKEN= agent-serve github forward …`) or `unset` it.
1029
+ `agent-serve github doctor` / `forward` warn when they detect this.
1030
+
1031
+ Pass `--repo owner/repo` / `--org ORG` to override the inferred target,
1032
+ `--events a,b,c` to narrow the set, `--url` for a custom endpoint (e.g. a
1033
+ tunnel), and `--slug` / `--channel` to forward to just one of several agents.
1034
+ Against a `--dev` server no secret is needed; set `GITHUB_WEBHOOK_SECRET` (or
1035
+ `--secret`) to exercise signature verification (required for a non-dev target).
1036
+ Only one forwarder per repo/org at a time (a GitHub limitation). Fixture replay
1037
+ still works too — `POST` a saved payload with an `x-github-event` header (no
1038
+ signature needed in `--dev`).
1039
+
1040
+ **No admin? Hillclimbing? Use `github replay`.** `gh webhook forward` needs repo
1041
+ admin and a live event. `agent-serve github replay <pr_url>` instead **reads**
1042
+ the PR (pull access is enough — no admin, no relay, and `GITHUB_TOKEN` is fine)
1043
+ and synthesizes GitHub-shaped payloads it POSTs straight at the channel:
1044
+
1045
+ ```bash
1046
+ # Replay a pull_request delivery for a PR to the discovered channel
1047
+ agent-serve github replay https://github.com/owner/repo/pull/123 --dir ./my-agent
1048
+
1049
+ # Replay everything the channel listens for (its declared events), CI failing
1050
+ agent-serve github replay owner/repo#123 --dir ./my-agent --events '*' --conclusion failure
1051
+
1052
+ # Inspect payloads without POSTing (and snapshot them as reusable fixtures)
1053
+ agent-serve github replay owner/repo#123 --dir ./my-agent --events '*' --dry-run --out fixtures/github
1054
+ ```
1055
+
1056
+ `--events` defaults to `pull_request` (`*` = the channel's declared events);
1057
+ `--action` / `--conclusion` / `--comment` / `--context` tune each synthesized
1058
+ event; `--secret` (or `GITHUB_WEBHOOK_SECRET`) signs them so a secret-configured
1059
+ channel verifies. Because replay sends a clean, signed loopback request, it
1060
+ passes both the `localDevStrict()` and `allowAll()`+signature auth modes — and
1061
+ it's fully deterministic, which is what hillclimbing wants.
1062
+
1063
+ **Production alternative: pull from Cursor (`--cursor-events`).** If the
1064
+ Cursor GitHub App is on the repo, `serve --cursor-events --repo owner/repo`
1065
+ long-polls `/v0/scm-events` with the host's Cursor account (no public URL /
1066
+ repo admin). Offset + consumer id live under `<state-root>/cursor-events/`.
1067
+ The stream is read as your Cursor user, so the flag **requires a signed-in
1068
+ host** (`agent-serve login`, `CURSOR_API_KEY`, or `serve({ apiKey })`) — serve
1069
+ fails fast rather than starting with a relay that can never receive events.
1070
+
1071
+ ### Hooks (`agent/hooks/*.ts`)
1072
+ Observe-only subscribers that run after each event is recorded — audit
1073
+ logs, metrics, mirroring transcripts into your own store. Keys are event
1074
+ types (or `*`); handler errors are logged, never fatal.
1075
+
1076
+ ### Schedules (`agent/schedules/*`)
1077
+
1078
+ Markdown form (fire-and-forget task session):
1079
+
1080
+ ```md
1081
+ ---
1082
+ cron: "0 9 * * 1-5"
1083
+ ---
1084
+
1085
+ Pull open incidents and post a summary to the metrics endpoint.
1086
+ ```
1087
+
1088
+ Handler form (full control, hand off to a channel):
1089
+
1090
+ ```ts
1091
+ import { defineSchedule } from "@cursor/july/schedules";
1092
+ import webhook from "../channels/webhook.js";
1093
+
1094
+ export default defineSchedule({
1095
+ cron: "*/30 * * * *",
1096
+ async run({ receive, waitUntil, appAuth, mcp }) {
1097
+ // optional: await mcp.callTool("units", "celsius_to_fahrenheit", { value: 0 });
1098
+ waitUntil(
1099
+ receive(webhook, {
1100
+ message: "Check for new critical alerts. Report only when there are any.",
1101
+ auth: appAuth,
1102
+ })
1103
+ );
1104
+ },
1105
+ });
1106
+ ```
1107
+
1108
+ Cron expressions are standard 5-field, evaluated in UTC with minute
1109
+ granularity. In production mode (`agent-serve serve`) schedules fire on
1110
+ cadence; in dev mode (`--dev`) they never fire automatically — dispatch one
1111
+ by hand, exactly once, through the same path production uses:
1112
+
1113
+ ```bash
1114
+ curl -X POST http://127.0.0.1:3000/<slug>/v1/dev/schedules/heartbeat
1115
+ # {"scheduleId":"heartbeat","sessionIds":["ses_..."]}
1116
+ ```
1117
+
1118
+ ## Sessions, events, and streaming
1119
+
1120
+ Two handles do two jobs: the **continuation token** resumes a conversation
1121
+ (owned by the channel; one active continuation per session, stale tokens
1122
+ rejected), and the **session id** streams and inspects it (owned by the
1123
+ runtime).
1124
+
1125
+ ### Reminders
1126
+
1127
+ Per-session durable wakes (distinct from deploy-time `agent/schedules/`):
1128
+
1129
+ ```ts
1130
+ await handle.createReminder({
1131
+ purpose: "ci_recheck",
1132
+ channelId: "drive",
1133
+ continuationToken: "pr-owner-repo-1",
1134
+ delay: "2h",
1135
+ prompt: "Re-check CI. Only act if still failing.",
1136
+ until: "Cancel once CI is green or the PR is merged.",
1137
+ });
1138
+ ```
1139
+
1140
+ Host/policy packs may pass `run` (return `stop` / `skip` / `delivered`)
1141
+ instead of prompts. In `--dev`, use `POST /v1/dev/reminders/:id` (or
1142
+ `handle.dispatchReminder`) to fire; auto-timers follow
1143
+ `ServeOptions.reminders` (default `!dev`). Run handlers are in-memory —
1144
+ after restart those reminders are disarmed (`handler_lost_on_restart`);
1145
+ re-arm from enroll/policy.
1146
+
1147
+ The NDJSON stream vocabulary — one JSON object per line, each carrying
1148
+ `{ type, index, sessionId, turnId?, at, data }`:
1149
+
1150
+ | Event | Meaning |
1151
+ | --------------------- | -------------------------------------------------------------- |
1152
+ | `session.started` | A durable session was created. |
1153
+ | `agent.bound` | Cursor agent id is known (`sdkAgentId`; cloud: `bc-…` + URL). |
1154
+ | `ab.assigned` | Sticky A/B enrollment (`experiment`, `variant` or `null` skip). |
1155
+ | `message.received` | An inbound user message was accepted. |
1156
+ | `turn.started` | A turn began. |
1157
+ | `step.started` / `step.completed` | Model step boundaries (with duration). |
1158
+ | `reasoning.appended` / `reasoning.completed` | Reasoning deltas and the block end. |
1159
+ | `message.appended` | Assistant text delta (with cumulative text so far). |
1160
+ | `message.completed` | A finalized assistant text block (`finishReason`: `stop` or `tool_call`). |
1161
+ | `actions.requested` | The model requested a tool call (streams before execution). |
1162
+ | `action.approval_requested` | A `needsApproval` tool is parked awaiting a human. |
1163
+ | `action.approval_resolved` | Human approved or denied the parked tool call. |
1164
+ | `action.result` | A tool call returned (`output`, `isError`). |
1165
+ | `subagent.called` / `subagent.completed` | Delegation to a subagent. |
1166
+ | `turn.completed` | The turn finished (`result`, `usage`). |
1167
+ | `turn.failed` | The turn failed (`message`). |
1168
+ | `session.waiting` | The session parked, ready for the next message. |
1169
+ | `session.completed` / `session.failed` | Terminal states for task-mode (schedule) sessions. |
1170
+
1171
+ The stream is durable: reconnect with `?startIndex=<n>` to replay from any
1172
+ point, or from 0 to rewind the whole session.
1173
+
1174
+ ## Serving programmatically
1175
+
1176
+ ```ts
1177
+ import { serve } from "@cursor/july";
1178
+
1179
+ const handle = await serve("./my-agent", {
1180
+ port: 3000,
1181
+ apiKey: process.env.CURSOR_API_KEY, // optional — see credential order below
1182
+ });
1183
+ console.log(`listening on ${handle.url}`);
1184
+ // handle.dispatchSchedule("heartbeat"), handle.project, await handle.close()
1185
+ ```
1186
+
1187
+ The Cursor credential resolves in one order everywhere (SDK turns, cloud
1188
+ runtime, Cursor account MCP connections): explicit `apiKey` option / `--api-key`
1189
+ → `CURSOR_API_KEY` → the key stored by `agent-serve login`. `whoami` shows
1190
+ which one is active; `logout` removes the stored key (revoke it in the
1191
+ Cursor dashboard to kill it outright).
1192
+
1193
+ Local-dev note: login/account RPCs honor `CURSOR_API_BASE_URL` while the
1194
+ Cursor SDK harness honors `CURSOR_BACKEND_URL`. When pointing at a
1195
+ non-production backend, set **both** to the same URL — a key minted on one
1196
+ backend is rejected by the other.
1197
+
1198
+ `serve` refuses to start when discovery produced error diagnostics; run
1199
+ `agent-serve validate` (or read `project.diagnostics`) to see why.
1200
+
1201
+ State lives under `<project>/.agent-serve/` (override with `stateRoot` /
1202
+ `--state-root`): `sessions/<id>/{session.json,events.ndjson,workspace/}`
1203
+ plus the SDK conversation store under `runner/`. Delete a session directory
1204
+ to forget that conversation.
1205
+
1206
+ Session workspaces are real Cursor project directories, so the harness also
1207
+ loads ambient project config from **ancestor** directories (nested
1208
+ `AGENTS.md` / `.cursor` rules and skills). That's usually what you want when
1209
+ the agent project is its own repository — but when it sits inside a large
1210
+ monorepo, either set `local: { cwd }` on `defineAgent` to a directory outside
1211
+ the monorepo (each session uses `<cwd>/<sessionId>`), or point `--state-root`
1212
+ somewhere outside (e.g. under `/tmp` or XDG state) so sessions don't inherit
1213
+ the monorepo's rules into context.
1214
+
1215
+ ## Not supported (yet)
1216
+
1217
+ Deliberately out of scope for now, and reported as warnings where the
1218
+ corresponding folder exists: per-subagent tools/skills/mcp-connections, nested
1219
+ subagents, WebSocket channel routes, platform channel packs for Discord
1220
+ and Teams (Slack is supported via `@cursor/july/channels/slack`;
1221
+ other platforms still use the authored `defineChannel` webhook form),
1222
+ OAuth-brokered MCP connections beyond the Cursor account
1223
+ (`{ cursorAccount: true }` covers connectors the account already
1224
+ authorized in Cursor), custom sandbox backends (`sandbox.ts`),
1225
+ instrumentation modules, dynamic per-caller capabilities, structured
1226
+ output schemas, and file uploads. Human-in-the-loop tool approvals
1227
+ (`needsApproval` on `execution: "server"` tools) are supported; exact
1228
+ resume of a parked tool call after host restart is not. Filesystem
1229
+ evals (`defineEval` under `evals/`), live A/B metrics (`defineAB` under
1230
+ `agent/ab`), and the `run` / `trajectory` commands are supported;
1231
+ LLM-as-judge scoring and external reporters are not yet. Context
1232
+ compaction is handled by the Cursor harness rather than
1233
+ configured here.