@cursor/july 0.1.94 → 0.1.96

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 (332) hide show
  1. package/AGENTS.md +3 -4
  2. package/dist/channels/deployments/types.d.ts +1 -1
  3. package/dist/channels/deployments/types.d.ts.map +1 -1
  4. package/dist/channels/deployments/types.js +1 -0
  5. package/dist/channels/github/github-channel.d.ts.map +1 -1
  6. package/dist/channels/github/github-channel.js +14 -2
  7. package/dist/channels/github/types.d.ts +18 -3
  8. package/dist/channels/github/types.d.ts.map +1 -1
  9. package/dist/channels/origin/api.d.ts +59 -13
  10. package/dist/channels/origin/api.d.ts.map +1 -1
  11. package/dist/channels/origin/api.js +168 -27
  12. package/dist/channels/origin/binding.d.ts +2 -2
  13. package/dist/channels/origin/checks.d.ts +6 -6
  14. package/dist/channels/origin/checks.js +6 -6
  15. package/dist/channels/origin/cursor-account.d.ts +5 -3
  16. package/dist/channels/origin/cursor-account.d.ts.map +1 -1
  17. package/dist/channels/origin/cursor-account.js +5 -3
  18. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  19. package/dist/channels/origin/origin-channel.js +11 -3
  20. package/dist/channels/origin/origin-webhook.d.ts +11 -1
  21. package/dist/channels/origin/origin-webhook.d.ts.map +1 -1
  22. package/dist/channels/origin/origin-webhook.js +23 -3
  23. package/dist/channels/origin/types.d.ts +8 -0
  24. package/dist/channels/origin/types.d.ts.map +1 -1
  25. package/dist/channels/slack/api.d.ts +9 -0
  26. package/dist/channels/slack/api.d.ts.map +1 -1
  27. package/dist/channels/slack/api.js +36 -5
  28. package/dist/channels/slack/defaults.d.ts.map +1 -1
  29. package/dist/channels/slack/defaults.js +17 -46
  30. package/dist/channels/slack/dispatch.d.ts +7 -1
  31. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  32. package/dist/channels/slack/dispatch.js +47 -23
  33. package/dist/channels/slack/inbound.d.ts +8 -0
  34. package/dist/channels/slack/inbound.d.ts.map +1 -1
  35. package/dist/channels/slack/inbound.js +32 -0
  36. package/dist/channels/slack/live-delivery.d.ts +26 -29
  37. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  38. package/dist/channels/slack/live-delivery.js +172 -73
  39. package/dist/channels/slack/manifest.d.ts.map +1 -1
  40. package/dist/channels/slack/manifest.js +5 -1
  41. package/dist/channels/slack/nudge-carrier.d.ts +2 -0
  42. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -1
  43. package/dist/channels/slack/nudge-carrier.js +48 -1
  44. package/dist/channels/slack/pr-ref.d.ts +7 -1
  45. package/dist/channels/slack/pr-ref.d.ts.map +1 -1
  46. package/dist/channels/slack/pr-ref.js +42 -23
  47. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  48. package/dist/channels/slack/slack-channel.js +2 -2
  49. package/dist/docs/404.html +2 -2
  50. package/dist/docs/ab.html +4 -4
  51. package/dist/docs/assets/{app.CFDEas4I.js → app.jXleCmOc.js} +1 -1
  52. package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +1 -0
  53. package/dist/docs/assets/chunks/{VPLocalSearchBox.B1IIYpYS.js → VPLocalSearchBox.8LhyglIT.js} +1 -1
  54. package/dist/docs/assets/chunks/{theme.Ct4NSiLm.js → theme.9ged-pr2.js} +2 -2
  55. package/dist/docs/assets/index.md.CI93Ltlb.js +5 -0
  56. package/dist/docs/assets/{index.md.DRakGHFe.lean.js → index.md.CI93Ltlb.lean.js} +1 -1
  57. package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.js → reference_cli.md.CCnG-nJx.js} +4 -2
  58. package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.lean.js → reference_cli.md.CCnG-nJx.lean.js} +1 -1
  59. package/dist/docs/assets/{reference_skills.md.8son6Hjm.js → reference_skills.md.BFW9retM.js} +3 -3
  60. package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.js → templates_agentic-owners.md.BZSH4N9z.js} +1 -1
  61. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.js +2 -0
  62. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.lean.js +1 -0
  63. package/dist/docs/assets/{templates_demo.md.DhFcWN6j.js → templates_demo.md.BeQX9V3H.js} +1 -1
  64. package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.js → templates_pr-autofixer.md.x5zl6-GT.js} +2 -2
  65. package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.lean.js → templates_pr-autofixer.md.x5zl6-GT.lean.js} +1 -1
  66. package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.js +4 -0
  67. package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.lean.js +1 -0
  68. package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.js → templates_security-reviewer.md.lshxbCLK.js} +2 -2
  69. package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.lean.js → templates_security-reviewer.md.lshxbCLK.lean.js} +1 -1
  70. package/dist/docs/assets/{templates_triage.md.CVlpctKS.js → templates_triage.md.Co4UNzkZ.js} +3 -3
  71. package/dist/docs/assets/{templates_triage.md.CVlpctKS.lean.js → templates_triage.md.Co4UNzkZ.lean.js} +1 -1
  72. package/dist/docs/building-with-agents.html +4 -4
  73. package/dist/docs/concepts.html +4 -4
  74. package/dist/docs/deployment.html +4 -4
  75. package/dist/docs/evals.html +4 -4
  76. package/dist/docs/guides/agent-to-agent.html +4 -4
  77. package/dist/docs/guides/cloud-runtime.html +4 -4
  78. package/dist/docs/guides/convert-automation.html +4 -4
  79. package/dist/docs/guides/github.html +4 -4
  80. package/dist/docs/guides/human-in-the-loop.html +4 -4
  81. package/dist/docs/guides/mcp-oauth.html +4 -4
  82. package/dist/docs/guides/opentelemetry.html +4 -4
  83. package/dist/docs/guides/slack.html +4 -4
  84. package/dist/docs/guides/webhooks.html +4 -4
  85. package/dist/docs/hashmap.json +1 -1
  86. package/dist/docs/hillclimbing.html +4 -4
  87. package/dist/docs/index.html +6 -6
  88. package/dist/docs/index.md +5 -1
  89. package/dist/docs/llms-full.txt +189 -26
  90. package/dist/docs/llms.txt +2 -0
  91. package/dist/docs/quickstart.html +4 -4
  92. package/dist/docs/reference/agent-config.html +4 -4
  93. package/dist/docs/reference/artifacts.html +4 -4
  94. package/dist/docs/reference/channels.html +4 -4
  95. package/dist/docs/reference/cli.html +8 -6
  96. package/dist/docs/reference/cli.md +4 -1
  97. package/dist/docs/reference/connections.html +4 -4
  98. package/dist/docs/reference/hooks.html +4 -4
  99. package/dist/docs/reference/http-api.html +4 -4
  100. package/dist/docs/reference/instructions.html +4 -4
  101. package/dist/docs/reference/playground.html +4 -4
  102. package/dist/docs/reference/project-layout.html +4 -4
  103. package/dist/docs/reference/prompt.html +4 -4
  104. package/dist/docs/reference/schedules.html +4 -4
  105. package/dist/docs/reference/sessions.html +4 -4
  106. package/dist/docs/reference/skills.html +7 -7
  107. package/dist/docs/reference/subagents.html +4 -4
  108. package/dist/docs/reference/tools.html +4 -4
  109. package/dist/docs/scaffolding-agents.html +4 -4
  110. package/dist/docs/storage.html +4 -4
  111. package/dist/docs/templates/agentic-owners.html +7 -7
  112. package/dist/docs/templates/agentic-owners.md +1 -1
  113. package/dist/docs/templates/code-wiki.html +28 -0
  114. package/dist/docs/templates/code-wiki.md +91 -0
  115. package/dist/docs/templates/demo.html +6 -6
  116. package/dist/docs/templates/demo.md +3 -2
  117. package/dist/docs/templates/pr-autofixer.html +6 -6
  118. package/dist/docs/templates/pr-autofixer.md +7 -13
  119. package/dist/docs/templates/security-help.html +30 -0
  120. package/dist/docs/templates/security-help.md +65 -0
  121. package/dist/docs/templates/security-reviewer.html +6 -6
  122. package/dist/docs/templates/security-reviewer.md +1 -2
  123. package/dist/docs/templates/triage.html +7 -7
  124. package/dist/docs/templates/triage.md +2 -6
  125. package/dist/docs/troubleshooting.html +4 -4
  126. package/dist/files-backends/cursor-hosted.d.ts +26 -0
  127. package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
  128. package/dist/files-backends/cursor-hosted.js +32 -4
  129. package/dist/files.d.ts +2 -0
  130. package/dist/files.d.ts.map +1 -1
  131. package/dist/files.js +5 -0
  132. package/dist/internal/advertise-tools.d.ts +2 -0
  133. package/dist/internal/advertise-tools.d.ts.map +1 -1
  134. package/dist/internal/advertise-tools.js +15 -1
  135. package/dist/internal/bounded.d.ts +7 -0
  136. package/dist/internal/bounded.d.ts.map +1 -0
  137. package/dist/internal/bounded.js +32 -0
  138. package/dist/internal/cli-deploy.d.ts.map +1 -1
  139. package/dist/internal/cli-deploy.js +142 -7
  140. package/dist/internal/conversation-mirror.d.ts +85 -0
  141. package/dist/internal/conversation-mirror.d.ts.map +1 -0
  142. package/dist/internal/conversation-mirror.js +312 -0
  143. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  144. package/dist/internal/cursor/backend-client.js +22 -48
  145. package/dist/internal/cursor/session-token.d.ts +45 -0
  146. package/dist/internal/cursor/session-token.d.ts.map +1 -0
  147. package/dist/internal/cursor/session-token.js +99 -0
  148. package/dist/internal/cursor-agent-template.d.ts +1 -1
  149. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  150. package/dist/internal/cursor-agent-template.js +1 -0
  151. package/dist/internal/deferred-channel-session.d.ts +20 -0
  152. package/dist/internal/deferred-channel-session.d.ts.map +1 -0
  153. package/dist/internal/deferred-channel-session.js +62 -0
  154. package/dist/internal/deploy-client.d.ts +13 -1
  155. package/dist/internal/deploy-client.d.ts.map +1 -1
  156. package/dist/internal/deploy-client.js +11 -1
  157. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  158. package/dist/internal/deploy-manifest.js +11 -5
  159. package/dist/internal/discovery.d.ts.map +1 -1
  160. package/dist/internal/discovery.js +22 -2
  161. package/dist/internal/framework-file-storage.d.ts +61 -0
  162. package/dist/internal/framework-file-storage.d.ts.map +1 -0
  163. package/dist/internal/framework-file-storage.js +204 -0
  164. package/dist/internal/framework-storage-selection.d.ts +38 -16
  165. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  166. package/dist/internal/framework-storage-selection.js +78 -17
  167. package/dist/internal/hosted-admission-context.d.ts +20 -0
  168. package/dist/internal/hosted-admission-context.d.ts.map +1 -0
  169. package/dist/internal/hosted-admission-context.js +31 -0
  170. package/dist/internal/hosted-delivery-protocol.d.ts +5 -0
  171. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  172. package/dist/internal/hosted-delivery-protocol.js +33 -1
  173. package/dist/internal/hosted-delivery.d.ts +11 -2
  174. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  175. package/dist/internal/hosted-delivery.js +79 -35
  176. package/dist/internal/hosted-managed-automation.d.ts +30 -0
  177. package/dist/internal/hosted-managed-automation.d.ts.map +1 -0
  178. package/dist/internal/hosted-managed-automation.js +58 -0
  179. package/dist/internal/init-project.d.ts.map +1 -1
  180. package/dist/internal/init-project.js +2 -0
  181. package/dist/internal/install-cursor-skills.d.ts.map +1 -1
  182. package/dist/internal/install-cursor-skills.js +1 -3
  183. package/dist/internal/managed-delivery.d.ts +80 -0
  184. package/dist/internal/managed-delivery.d.ts.map +1 -0
  185. package/dist/internal/managed-delivery.js +86 -0
  186. package/dist/internal/review-comments.d.ts.map +1 -1
  187. package/dist/internal/review-comments.js +10 -98
  188. package/dist/internal/runtime-dispatch-runner.d.ts +5 -1
  189. package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
  190. package/dist/internal/runtime-dispatch-runner.js +7 -0
  191. package/dist/internal/scm/pr-url.d.ts +14 -0
  192. package/dist/internal/scm/pr-url.d.ts.map +1 -0
  193. package/dist/internal/scm/pr-url.js +65 -0
  194. package/dist/internal/sdk-runner.d.ts +25 -0
  195. package/dist/internal/sdk-runner.d.ts.map +1 -1
  196. package/dist/internal/sdk-runner.js +70 -0
  197. package/dist/internal/server.d.ts.map +1 -1
  198. package/dist/internal/server.js +11 -2
  199. package/dist/internal/session-engine.d.ts +13 -0
  200. package/dist/internal/session-engine.d.ts.map +1 -1
  201. package/dist/internal/session-engine.js +71 -3
  202. package/dist/internal/steering.d.ts +15 -0
  203. package/dist/internal/steering.d.ts.map +1 -0
  204. package/dist/internal/steering.js +18 -0
  205. package/dist/playground/assets/index-B1DDvBeC.css +1 -0
  206. package/dist/playground/assets/{index-B3JCyigB.js → index-Cx9CclK_.js} +47 -47
  207. package/dist/playground/index.html +2 -2
  208. package/dist/storage-backends/cursor-hosted.d.ts +8 -0
  209. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  210. package/dist/storage-backends/cursor-hosted.js +4 -3
  211. package/dist/types.d.ts +2 -1
  212. package/dist/types.d.ts.map +1 -1
  213. package/docs/README.md +5 -1
  214. package/docs/reference/cli.md +4 -1
  215. package/docs/templates/agentic-owners.md +1 -1
  216. package/docs/templates/code-wiki.md +96 -0
  217. package/docs/templates/demo.md +3 -2
  218. package/docs/templates/pr-autofixer.md +7 -13
  219. package/docs/templates/security-help.md +70 -0
  220. package/docs/templates/security-reviewer.md +1 -2
  221. package/docs/templates/triage.md +2 -6
  222. package/package.json +2 -2
  223. package/skills/ab/SKILL.md +13 -97
  224. package/skills/create-agent/SKILL.md +123 -223
  225. package/skills/debug/SKILL.md +34 -68
  226. package/skills/evals/SKILL.md +55 -180
  227. package/skills/framework-map/SKILL.md +83 -145
  228. package/skills/github/SKILL.md +53 -111
  229. package/skills/hillclimb/SKILL.md +40 -163
  230. package/skills/mcp-auth/SKILL.md +48 -107
  231. package/skills/otel/SKILL.md +22 -68
  232. package/skills/setup-slack/SKILL.md +77 -208
  233. package/src/channels/deployments/types.ts +1 -0
  234. package/src/channels/github/github-channel.ts +23 -8
  235. package/src/channels/github/types.ts +19 -2
  236. package/src/channels/origin/api.ts +242 -30
  237. package/src/channels/origin/binding.ts +2 -2
  238. package/src/channels/origin/checks.ts +6 -6
  239. package/src/channels/origin/cursor-account.ts +5 -3
  240. package/src/channels/origin/origin-channel.ts +13 -1
  241. package/src/channels/origin/origin-webhook.ts +27 -3
  242. package/src/channels/origin/types.ts +8 -0
  243. package/src/channels/slack/api.ts +41 -5
  244. package/src/channels/slack/defaults.ts +15 -49
  245. package/src/channels/slack/dispatch.ts +56 -22
  246. package/src/channels/slack/inbound.ts +43 -0
  247. package/src/channels/slack/live-delivery.ts +235 -114
  248. package/src/channels/slack/manifest.ts +5 -1
  249. package/src/channels/slack/nudge-carrier.ts +48 -1
  250. package/src/channels/slack/pr-ref.ts +56 -25
  251. package/src/channels/slack/slack-channel.ts +2 -0
  252. package/src/files-backends/cursor-hosted.ts +41 -2
  253. package/src/files.ts +11 -0
  254. package/src/internal/advertise-tools.ts +18 -2
  255. package/src/internal/bounded.ts +28 -0
  256. package/src/internal/cli-deploy.ts +183 -7
  257. package/src/internal/conversation-mirror.ts +406 -0
  258. package/src/internal/cursor/backend-client.ts +34 -58
  259. package/src/internal/cursor/session-token.ts +115 -0
  260. package/src/internal/cursor-agent-template.ts +1 -0
  261. package/src/internal/deferred-channel-session.ts +61 -0
  262. package/src/internal/deploy-client.ts +24 -1
  263. package/src/internal/deploy-manifest.ts +10 -5
  264. package/src/internal/discovery.ts +25 -2
  265. package/src/internal/framework-file-storage.ts +222 -0
  266. package/src/internal/framework-storage-selection.ts +101 -19
  267. package/src/internal/hosted-admission-context.ts +37 -0
  268. package/src/internal/hosted-delivery-protocol.ts +44 -1
  269. package/src/internal/hosted-delivery.ts +105 -40
  270. package/src/internal/hosted-managed-automation.ts +72 -0
  271. package/src/internal/init-project.ts +2 -0
  272. package/src/internal/install-cursor-skills.ts +1 -4
  273. package/src/internal/managed-delivery.ts +125 -0
  274. package/src/internal/review-comments.ts +10 -113
  275. package/src/internal/runtime-dispatch-runner.ts +10 -0
  276. package/src/internal/scm/pr-url.ts +95 -0
  277. package/src/internal/sdk-runner.ts +103 -0
  278. package/src/internal/server.ts +12 -0
  279. package/src/internal/session-engine.ts +76 -4
  280. package/src/internal/steering.ts +17 -0
  281. package/src/storage-backends/cursor-hosted.ts +10 -1
  282. package/src/types.ts +2 -1
  283. package/templates/agentic-owners/README.md +1 -1
  284. package/templates/agentic-owners/agent/agent.ts +0 -10
  285. package/templates/agentic-owners/agent/channels/github.ts +5 -14
  286. package/templates/agentic-owners/agent/lib/config.ts +0 -8
  287. package/templates/agentic-owners/agent/lib/review.ts +2 -15
  288. package/templates/agentic-owners/agent/tools/record_review.ts +2 -4
  289. package/templates/code-wiki/README.md +34 -0
  290. package/templates/code-wiki/agent/agent.ts +8 -0
  291. package/templates/code-wiki/agent/channels/github.ts +144 -0
  292. package/templates/code-wiki/agent/instructions.md +39 -0
  293. package/templates/code-wiki/agent/lib/repos.ts +5 -0
  294. package/templates/code-wiki/agent/lib/wiki.ts +22 -0
  295. package/templates/code-wiki/agent/tools/apply_wiki.ts +219 -0
  296. package/templates/code-wiki/evals/evals.config.ts +5 -0
  297. package/templates/code-wiki/evals/wiki.eval.ts +132 -0
  298. package/templates/code-wiki/init.json +25 -0
  299. package/templates/code-wiki/package.json +18 -0
  300. package/templates/code-wiki/tsconfig.json +12 -0
  301. package/templates/demo/agent/agent.ts +0 -10
  302. package/templates/pr-autofixer/README.md +0 -2
  303. package/templates/pr-autofixer/agent/agent.ts +0 -11
  304. package/templates/pr-autofixer/agent/channels/slack.ts +0 -7
  305. package/templates/pr-autofixer/agent/lib/pr-state.ts +5 -17
  306. package/templates/pr-autofixer/agent/lib/repos.ts +0 -1
  307. package/templates/security-help/README.md +2 -2
  308. package/templates/security-help/agent/agent.ts +1 -2
  309. package/templates/security-help/agent/channels/slack.ts +0 -3
  310. package/templates/security-help/agent/instructions.md +9 -10
  311. package/templates/security-help/agent/skills/access-request.md +1 -1
  312. package/templates/security-help/agent/skills/faq.md +31 -0
  313. package/templates/security-help/agent/skills/security-playbooks.md +1 -1
  314. package/templates/security-help/package.json +1 -2
  315. package/templates/security-reviewer/agent/agent.ts +0 -10
  316. package/templates/triage/agent/agent.ts +0 -10
  317. package/templates/triage/agent/channels/queue.ts +1 -1
  318. package/templates/triage/agent/channels/webhook.ts +1 -3
  319. package/dist/docs/assets/chunks/@localSearchIndexroot.DU3U2Ij2.js +0 -1
  320. package/dist/docs/assets/index.md.DRakGHFe.js +0 -5
  321. package/dist/internal/deployment-scm-event-stream.d.ts +0 -116
  322. package/dist/internal/deployment-scm-event-stream.d.ts.map +0 -1
  323. package/dist/internal/deployment-scm-event-stream.js +0 -474
  324. package/dist/playground/assets/index-D9N7-q97.css +0 -1
  325. package/src/internal/deployment-scm-event-stream.ts +0 -593
  326. package/templates/security-help/agent/knowledge/faq/approvals.md +0 -5
  327. package/templates/security-help/agent/knowledge/faq/channels.md +0 -6
  328. package/templates/security-help/agent/knowledge/faq/phishing.md +0 -10
  329. package/templates/security-help/agent/skills/security-first-pass.md +0 -15
  330. /package/dist/docs/assets/{reference_skills.md.8son6Hjm.lean.js → reference_skills.md.BFW9retM.lean.js} +0 -0
  331. /package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.lean.js → templates_agentic-owners.md.BZSH4N9z.lean.js} +0 -0
  332. /package/dist/docs/assets/{templates_demo.md.DhFcWN6j.lean.js → templates_demo.md.BeQX9V3H.lean.js} +0 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Provider-neutral PR URL parse. GitHub.com plus the Origin page shapes
3
+ * humans paste: cursor.com/codebase, review.cursor.com/pr, origin.cursor.com.
4
+ */
5
+
6
+ export type PrProvider = "github" | "origin";
7
+
8
+ export type ParsedPrUrl = {
9
+ provider: PrProvider;
10
+ owner: string;
11
+ repo: string;
12
+ number: number;
13
+ url: string;
14
+ };
15
+
16
+ const ORIGIN_PAGE = /^origin(-[a-z0-9]+)?\.cursor\.com$/i;
17
+
18
+ function positiveInt(raw: string | undefined): number | undefined {
19
+ if (raw === undefined) {
20
+ return undefined;
21
+ }
22
+ const n = Number(raw);
23
+ if (!Number.isInteger(n) || n <= 0) {
24
+ return undefined;
25
+ }
26
+ return n;
27
+ }
28
+
29
+ function parsed(
30
+ provider: PrProvider,
31
+ owner: string | undefined,
32
+ repo: string | undefined,
33
+ number: number | undefined
34
+ ): ParsedPrUrl | undefined {
35
+ if (owner === undefined || repo === undefined || number === undefined) {
36
+ return undefined;
37
+ }
38
+ const url =
39
+ provider === "origin"
40
+ ? `https://cursor.com/codebase/${owner}/${repo}/pull/${number}`
41
+ : `https://github.com/${owner}/${repo}/pull/${number}`;
42
+ return { provider, owner, repo, number, url };
43
+ }
44
+
45
+ /** Strip Slack mrkdwn wrappers and trailing punctuation before `URL`. */
46
+ function normalizeRaw(raw: string): string {
47
+ return raw
48
+ .trim()
49
+ .replace(/^[<[]+/, "")
50
+ .replace(/[>\].,;]+$/, "");
51
+ }
52
+
53
+ export function parsePrUrl(raw: string): ParsedPrUrl | undefined {
54
+ let url: URL;
55
+ try {
56
+ url = new URL(normalizeRaw(raw));
57
+ } catch {
58
+ return undefined;
59
+ }
60
+
61
+ const parts = url.pathname.split("/").filter(Boolean);
62
+ const host = url.hostname.toLowerCase();
63
+
64
+ if (
65
+ (host === "github.com" || host === "www.github.com") &&
66
+ parts.length >= 4 &&
67
+ parts[2] === "pull"
68
+ ) {
69
+ return parsed("github", parts[0], parts[1], positiveInt(parts[3]));
70
+ }
71
+
72
+ if (ORIGIN_PAGE.test(host) && parts.length >= 4 && parts[2] === "github") {
73
+ return parsed("origin", parts[0], parts[1], positiveInt(parts[3]));
74
+ }
75
+
76
+ if (
77
+ (host === "cursor.com" || host === "www.cursor.com") &&
78
+ parts[0] === "codebase" &&
79
+ parts.length >= 5 &&
80
+ parts[3] === "pull"
81
+ ) {
82
+ return parsed("origin", parts[1], parts[2], positiveInt(parts[4]));
83
+ }
84
+
85
+ if (host === "review.cursor.com" && parts[0] === "pr") {
86
+ if (parts.length >= 5 && parts[3] === "github") {
87
+ return parsed("origin", parts[1], parts[2], positiveInt(parts[4]));
88
+ }
89
+ if (parts.length >= 4) {
90
+ return parsed("origin", parts[1], parts[2], positiveInt(parts[3]));
91
+ }
92
+ }
93
+
94
+ return undefined;
95
+ }
@@ -16,6 +16,7 @@ import {
16
16
  createAgentPlatform,
17
17
  type LocalAgentStore,
18
18
  type ModelSelection,
19
+ type Run,
19
20
  type RunResult,
20
21
  type SDKAgent,
21
22
  } from "@cursor/sdk";
@@ -29,11 +30,20 @@ import {
29
30
  type TurnUsage,
30
31
  } from "../types.js";
31
32
  import { mapSdkTokenUsage, usageTokenSum } from "./cloud-turn-cost.js";
33
+ import type { ConversationMirror } from "./conversation-mirror.js";
32
34
  import { resolveApiKeySync } from "./cursor/credentials.js";
35
+ import { describeError } from "./describe-error.js";
36
+
37
+ /** Mirrors @cursor/sdk's SteerAckOutcome; the published dist may predate it. */
38
+ export type SteerAckOutcome = "complete_delivered" | "revert_to_followup";
33
39
 
34
40
  /** Internal seam between the session engine and the Cursor SDK harness. */
35
41
  export interface AgentRunner {
36
42
  runTurn(request: RunnerTurnRequest): Promise<TurnOutcome>;
43
+ steer?(request: {
44
+ sessionId: string;
45
+ text: string;
46
+ }): Promise<SteerAckOutcome>;
37
47
  /**
38
48
  * Resolve a local workspace before any session needs it, so the first turn
39
49
  * is not the one that pays for it. Best effort, hence the reported outcome:
@@ -117,6 +127,13 @@ export interface CursorSdkRunnerOptions {
117
127
  * `LocalAgentStore` for heavy multi-session concurrency.
118
128
  */
119
129
  store?: LocalAgentStore;
130
+ /**
131
+ * Durable mirror for local conversations (v2 hosted engines). Hydrated
132
+ * before `Agent.resume` so a follow-up landing on a fresh pod finds the
133
+ * conversation, and persisted — awaited — after each local turn settles,
134
+ * because a hosted pod can be parked right after the execution reply.
135
+ */
136
+ conversationMirror?: ConversationMirror;
120
137
  /** Diagnostics for degraded paths (resume-miss fallback). */
121
138
  logger?: (line: string) => void;
122
139
  /**
@@ -185,6 +202,7 @@ function retryReason(
185
202
 
186
203
  export class CursorSdkRunner implements AgentRunner {
187
204
  private readonly agents = new Map<string, CachedAgentHandle>();
205
+ private readonly activeRuns = new Map<string, Run>();
188
206
  private readonly stores = new Map<string, Promise<LocalAgentStore>>();
189
207
  private readonly prewarmReleases: Array<() => Promise<void>> = [];
190
208
 
@@ -314,6 +332,7 @@ export class CursorSdkRunner implements AgentRunner {
314
332
  ) {
315
333
  await request.onRequestId(run.requestId);
316
334
  }
335
+ this.activeRuns.set(request.sessionId, run);
317
336
  try {
318
337
  const result = await run.wait();
319
338
  let outcome = toTurnOutcome(result);
@@ -338,7 +357,25 @@ export class CursorSdkRunner implements AgentRunner {
338
357
  }
339
358
  return outcome;
340
359
  } finally {
360
+ if (this.activeRuns.get(request.sessionId) === run) {
361
+ this.activeRuns.delete(request.sessionId);
362
+ }
341
363
  signal?.removeEventListener("abort", onAbort);
364
+ // Mirror the conversation before replying: on hosted v2 the pod can
365
+ // be parked as soon as the execution reply lands, so a
366
+ // fire-and-forget write would race VM teardown. In the `finally` so
367
+ // a `wait()` that THROWS (transport death mid-turn, an abort with
368
+ // no follow-up) still persists the checkpoints the turn already
369
+ // wrote — the store is local SQLite, safe to read on the throw
370
+ // path, and persistConversation swallows its own errors. Guarded
371
+ // here (not just inside the helper) so unmirrored turns keep their
372
+ // exact post-wait sequencing.
373
+ if (
374
+ request.runtime === "local" &&
375
+ this.options.conversationMirror !== undefined
376
+ ) {
377
+ await this.persistConversation(request, agent.agentId);
378
+ }
342
379
  }
343
380
  } catch (error) {
344
381
  // Drop the cached agent so a dead Connect/HTTP2 session cannot poison
@@ -348,6 +385,28 @@ export class CursorSdkRunner implements AgentRunner {
348
385
  }
349
386
  }
350
387
 
388
+ async steer(request: {
389
+ sessionId: string;
390
+ text: string;
391
+ }): Promise<SteerAckOutcome> {
392
+ const run = this.activeRuns.get(request.sessionId) as
393
+ | (Run & {
394
+ steer?: (text: string) => Promise<SteerAckOutcome>;
395
+ })
396
+ | undefined;
397
+ if (run?.steer === undefined) {
398
+ return "revert_to_followup";
399
+ }
400
+ try {
401
+ return await run.steer(request.text);
402
+ } catch (error) {
403
+ this.log(
404
+ `[agent-sdk] session ${request.sessionId}: steer failed: ${describeError(error)}`
405
+ );
406
+ return "revert_to_followup";
407
+ }
408
+ }
409
+
351
410
  /**
352
411
  * Build the local executor for a workspace before any session asks for it.
353
412
  * The warmed executor is reference counted, so the lease is held until
@@ -469,6 +528,9 @@ export class CursorSdkRunner implements AgentRunner {
469
528
  private async buildLocalOptions(
470
529
  request: RunnerPrewarmRequest
471
530
  ): Promise<AgentOptions> {
531
+ // Not resolveStore: an injected store is used synchronously (no
532
+ // microtask, no thenable assimilation), exactly as before the mirror
533
+ // existed.
472
534
  const store =
473
535
  this.options.store ?? (await this.getStore(request.runnerStateDir));
474
536
  return {
@@ -507,6 +569,15 @@ export class CursorSdkRunner implements AgentRunner {
507
569
  }
508
570
 
509
571
  const agentId = request.sdkAgentId ?? request.sessionId;
572
+ const mirror = this.options.conversationMirror;
573
+ if (mirror !== undefined) {
574
+ // Host replacement: restore the conversation from the durable mirror
575
+ // before resuming, so the thread continues instead of starting fresh.
576
+ // A sink failure propagates and fails the turn (retryable) — reading
577
+ // an outage as "nothing mirrored" would silently fork the thread.
578
+ const store = await this.resolveStore(request);
579
+ await mirror.hydrate(store, agentId);
580
+ }
510
581
  try {
511
582
  return await Agent.resume(agentId, options);
512
583
  } catch (error) {
@@ -570,6 +641,38 @@ export class CursorSdkRunner implements AgentRunner {
570
641
  return Agent.create(options);
571
642
  }
572
643
 
644
+ private resolveStore(
645
+ request: Pick<RunnerPrewarmRequest, "runnerStateDir">
646
+ ): Promise<LocalAgentStore> {
647
+ return this.options.store !== undefined
648
+ ? Promise.resolve(this.options.store)
649
+ : this.getStore(request.runnerStateDir);
650
+ }
651
+
652
+ /**
653
+ * Mirror the settled turn's conversation. Persist failures are logged and
654
+ * dropped — a durable-mirror outage must not fail a turn that already ran.
655
+ */
656
+ private async persistConversation(
657
+ request: RunnerTurnRequest,
658
+ sdkAgentId: string
659
+ ): Promise<void> {
660
+ const mirror = this.options.conversationMirror;
661
+ if (mirror === undefined) {
662
+ return;
663
+ }
664
+ try {
665
+ const store = await this.resolveStore(request);
666
+ await mirror.persist(store, sdkAgentId);
667
+ } catch (error) {
668
+ this.log(
669
+ `[agent-sdk] conversation mirror: persist for ${sdkAgentId} failed: ${
670
+ error instanceof Error ? error.message : String(error)
671
+ }`
672
+ );
673
+ }
674
+ }
675
+
573
676
  private getStore(stateDir: string): Promise<LocalAgentStore> {
574
677
  let storePromise = this.stores.get(stateDir);
575
678
  if (storePromise === undefined) {
@@ -51,6 +51,7 @@ import type {
51
51
  ServeOptions,
52
52
  } from "../types.js";
53
53
  import { V2EngineError } from "./continuation-identity.js";
54
+ import { selectConversationMirror } from "./conversation-mirror.js";
54
55
  import {
55
56
  type CursorAccountConnectionRef,
56
57
  CursorAccountMcpBridge,
@@ -516,12 +517,21 @@ export async function startServer(
516
517
  "[agent-sdk] warning: --allow-anonymous admits every HTTP caller as the same principal; use only on a trusted network (e.g. Tailscale) and prefer --bearer-token for shared hosts"
517
518
  );
518
519
  }
520
+ // v2 hosted engines mirror the SDK conversation store to the deployment's
521
+ // shared file storage so a fresh pod resumes threads instead of forking
522
+ // them. Hosted serve is single-agent; a multi-mount serve (dev) has no
523
+ // single agent name to mint keys under and does not mirror.
524
+ const conversationMirror =
525
+ mounts.length === 1 && mounts[0] !== undefined
526
+ ? selectConversationMirror({ project: mounts[0].project, logger })
527
+ : undefined;
519
528
  const sdkRunner =
520
529
  options.runner ??
521
530
  new CursorSdkRunner({
522
531
  ...(options.apiKey === undefined || options.apiKey.trim() === ""
523
532
  ? {}
524
533
  : { apiKey: options.apiKey }),
534
+ ...(conversationMirror === undefined ? {} : { conversationMirror }),
525
535
  logger,
526
536
  });
527
537
  // Grok Bot turns run on Cursor's hosted Grok Bot (Sand) harness through
@@ -1276,6 +1286,8 @@ function mountAgentRoutes(
1276
1286
  channelId,
1277
1287
  hostedSessionId,
1278
1288
  }),
1289
+ drainSessionDurability: (sessionId) =>
1290
+ engine.drainSessionDurability(sessionId),
1279
1291
  });
1280
1292
  }
1281
1293
  // Playground lists/opens Slack (and other channel) sessions across
@@ -113,6 +113,10 @@ import {
113
113
  createHostSlackClient,
114
114
  type HostPlatforms,
115
115
  } from "./host-platforms.js";
116
+ import {
117
+ HostedAdmissionLocalSendError,
118
+ isDuringHostedAdmission,
119
+ } from "./hosted-admission-context.js";
116
120
  import {
117
121
  formatContinuationToken,
118
122
  newContinuationKey,
@@ -132,10 +136,11 @@ import {
132
136
  SCHEDULE_CHANNEL_PREFIX,
133
137
  } from "./prompt-context.js";
134
138
  import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections.js";
135
- import type { AgentRunner } from "./sdk-runner.js";
139
+ import type { AgentRunner, SteerAckOutcome } from "./sdk-runner.js";
136
140
  import { sessionExternalLinks } from "./session-links.js";
137
141
  import { SessionStore } from "./session-store.js";
138
142
  import { syncSkillsToStore } from "./skills-sync.js";
143
+ import { steeringEnabled } from "./steering.js";
139
144
  import { StorageCoordinator } from "./storage-coordinator.js";
140
145
  import type { FrameworkSessionStore } from "./storage-roles.js";
141
146
  import { decideToolCall, resolveEffect } from "./tool-policy.js";
@@ -465,6 +470,7 @@ export class SessionEngine {
465
470
  // for why the choice does not belong to the agent author.
466
471
  const frameworkStorage = selectFrameworkStorageDefinition({
467
472
  authored: options.project.storage,
473
+ architecture: options.project.agent.architecture,
468
474
  });
469
475
  this.storage =
470
476
  frameworkStorage === undefined
@@ -693,6 +699,9 @@ export class SessionEngine {
693
699
  ) {
694
700
  return;
695
701
  }
702
+ if (steeringEnabled()) {
703
+ return;
704
+ }
696
705
  try {
697
706
  await materializeSlackNudgeCarrier({
698
707
  harnessCwd,
@@ -811,6 +820,13 @@ export class SessionEngine {
811
820
  message: string,
812
821
  options: EngineSendOptions = {}
813
822
  ): Promise<ChannelSession> {
823
+ if (isDuringHostedAdmission()) {
824
+ // Only sends that bypassed the admission capture stub (typically a
825
+ // startup-captured `onStart` send) reach the engine inside the
826
+ // admission window; running one would leak an invisible local session
827
+ // on a sessionless pod.
828
+ throw new HostedAdmissionLocalSendError();
829
+ }
814
830
  const identity =
815
831
  channelId === HTTP_CHANNEL_ID
816
832
  ? httpContinuationIdentity(this.project.httpChannel)
@@ -1347,18 +1363,52 @@ export class SessionEngine {
1347
1363
  });
1348
1364
  }
1349
1365
 
1366
+ private async steerIntoRunningTurn(
1367
+ sessionId: string,
1368
+ message: string
1369
+ ): Promise<SteerAckOutcome> {
1370
+ if (this.runner.steer === undefined) {
1371
+ return "revert_to_followup";
1372
+ }
1373
+ try {
1374
+ return await this.runner.steer({ sessionId, text: message });
1375
+ } catch (error) {
1376
+ this.logger(
1377
+ `[session] steer failed for ${sessionId}: ${describeError(error)}`
1378
+ );
1379
+ return "revert_to_followup";
1380
+ }
1381
+ }
1382
+
1350
1383
  private async admitTurn(
1351
1384
  sessionId: string,
1352
1385
  message: string,
1353
1386
  options: AdmitTurnOptions = {}
1354
1387
  ): Promise<boolean> {
1355
1388
  const admission = options.admission ?? "preempt";
1389
+ if (
1390
+ admission === "coalesce" &&
1391
+ steeringEnabled() &&
1392
+ this.isTurnRunning(sessionId)
1393
+ ) {
1394
+ const toolCtx = this.activeToolContexts.get(sessionId);
1395
+ const steerOutcome = await this.steerIntoRunningTurn(sessionId, message);
1396
+ if (steerOutcome === "complete_delivered") {
1397
+ this.logger(`[session] steered into running turn ${sessionId}`);
1398
+ if (toolCtx !== undefined) {
1399
+ toolCtx.emit({ type: "message.received", data: { text: message } });
1400
+ toolCtx.emit({ type: "nudge.delivered", data: {} });
1401
+ }
1402
+ return true;
1403
+ }
1404
+ }
1405
+ // Re-checked after the steer: a turn that ended during it falls through
1406
+ // and this message starts the next turn itself. Enqueueing here instead
1407
+ // would orphan it — the completion drain already ran.
1356
1408
  if (admission === "coalesce" && this.isTurnRunning(sessionId)) {
1357
1409
  this.enqueueCoalesced(sessionId, {
1358
1410
  message,
1359
- ...(options.sourceTs === undefined
1360
- ? {}
1361
- : { sourceTs: options.sourceTs }),
1411
+ sourceTs: options.sourceTs,
1362
1412
  });
1363
1413
  this.logger(`[session] coalesce enqueue ${sessionId}`);
1364
1414
  return true;
@@ -3382,6 +3432,28 @@ export class SessionEngine {
3382
3432
  // Session handles
3383
3433
  // ==========================================================================
3384
3434
 
3435
+ /**
3436
+ * Wait until everything this session has already emitted is durably
3437
+ * mirrored: its dispatch chain first (the chain's tail runs the turn-end
3438
+ * storage flush, so the record carries the boundary event's handler
3439
+ * mutations), then the storage delivery queue. Hosted v2 awaits this
3440
+ * before the execution reply — the pod can be parked the moment the
3441
+ * reply lands, and an unflushed session record would strand a
3442
+ * conversation whose blobs persisted but whose record (which selects the
3443
+ * conversation to hydrate) never reached the sink. No-op without a
3444
+ * storage mirror.
3445
+ */
3446
+ async drainSessionDurability(sessionId: string): Promise<void> {
3447
+ if (this.storage === undefined) {
3448
+ return;
3449
+ }
3450
+ const chain = this.dispatchChains.get(sessionId);
3451
+ if (chain !== undefined) {
3452
+ await chain.catch(() => {});
3453
+ }
3454
+ await this.storage.whenIdle();
3455
+ }
3456
+
3385
3457
  async waitForCompletion(sessionId: string): Promise<TurnOutcome> {
3386
3458
  // Drain admission first so pendingTurns → activeTurns is visible before we
3387
3459
  // decide the session is idle (reminders use this for queueBehindTurn).
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Deploy-time switch between the two mid-turn follow-up carriers.
3
+ *
4
+ * With ENABLE_STEERING set ("1"/"true"), a Slack follow-up arriving mid-turn
5
+ * is steered into the running turn via Run.steer and the nudge-carrier file
6
+ * store is left uninstalled. Unset (the default), the pre-steering behavior
7
+ * is fully preserved: the carrier is materialized and follow-ups ride the
8
+ * nudge store to the running turn's next tool boundary.
9
+ *
10
+ * Read per call rather than cached so tests can flip it, and because this is
11
+ * a rollout switch: once steering has proven out in production the flag and
12
+ * the carrier both go away.
13
+ */
14
+ export function steeringEnabled(): boolean {
15
+ const value = process.env.ENABLE_STEERING?.trim().toLowerCase();
16
+ return value === "1" || value === "true";
17
+ }
@@ -76,6 +76,14 @@ export interface CursorHostedStorageOptions {
76
76
  baseUrl?: string;
77
77
  /** Bearer credential (default: `CURSOR_API_KEY`). */
78
78
  apiKey?: string;
79
+ /**
80
+ * Lazy credential, re-resolved on every call and preferred over
81
+ * {@link apiKey}: hosted freeze snapshots listen unsigned and bind the
82
+ * key file after serve start, so a value captured at construct time
83
+ * would be permanently absent. Used by the platform-selected v2
84
+ * defaults; kept separate so `apiKey` stays a plain string for authors.
85
+ */
86
+ resolveApiKey?: () => string | undefined;
79
87
  /** Injected for tests. */
80
88
  fetch?: typeof fetch;
81
89
  }
@@ -180,7 +188,8 @@ function toCasResult(response: KvCasResponse): KvCasResult {
180
188
  }
181
189
 
182
190
  function resolveApiKey(options: CursorHostedStorageOptions): string {
183
- const key = (options.apiKey ?? process.env.CURSOR_API_KEY)?.trim();
191
+ const configured = options.resolveApiKey?.() ?? options.apiKey;
192
+ const key = (configured ?? process.env.CURSOR_API_KEY)?.trim();
184
193
  if (key === undefined || key === "") {
185
194
  throw new Error(
186
195
  "cursorHostedStorage requires CURSOR_API_KEY (deployment pod credential on Cursor hosting)"
package/src/types.ts CHANGED
@@ -1262,7 +1262,8 @@ export type ConnectionConfig = (
1262
1262
  * Local runtime only, like all server tools: a `runtime: "cloud"` agent
1263
1263
  * fails discovery, and hybrid per-send cloud sessions refuse the turn.
1264
1264
  * The listing runs once per turn; point this at endpoints that can
1265
- * answer `tools/list` cheaply.
1265
+ * answer `tools/list` cheaply. Tools whose MCP `annotations.title` is
1266
+ * `do-not-call` are omitted (vendor host-UI / MCP Apps helpers).
1266
1267
  */
1267
1268
  advertiseTools?: boolean;
1268
1269
  /**
@@ -58,7 +58,7 @@ policy only) opts paths out entirely.
58
58
  - One model turn per PR head assesses the diff against the base rubric
59
59
  (`agent/lib/owners/prompts/base-review.md`) plus matched rule prompts.
60
60
  - The model records a verdict with `record_review`; the host recomputes
61
- the policy and applies the deterministic decision approve, or request
61
+ the policy and applies the deterministic decision: approve, or request
62
62
  the policy owners (max 2, never the author) and leave one comment it
63
63
  keeps updated.
64
64
  - The model never writes to GitHub. Broken policy files fail closed with
@@ -1,18 +1,8 @@
1
- import { homedir } from "node:os";
2
- import { join } from "node:path";
3
1
  import { defineAgent } from "@cursor/july";
4
2
 
5
3
  export default defineAgent({
6
- model: {
7
- id: "grok-4.5",
8
- params: [
9
- { id: "effort", value: "high" },
10
- { id: "fast", value: "true" },
11
- ],
12
- },
13
4
  tools: ["read", "grep", "glob", "ls"],
14
5
  local: {
15
6
  sandbox: true,
16
- cwd: join(homedir(), ".cache", "agent-serve", "agentic-owners"),
17
7
  },
18
8
  });
@@ -22,10 +22,8 @@ const REVIEW_ACTIONS = new Set([
22
22
  let log: (line: string) => void = () => {};
23
23
 
24
24
  export default githubChannel({
25
- // With repos configured, the signed-in Cursor account supplies both the
26
- // PR event stream and a repo-scoped GitHub credential — no webhook or
27
- // GitHub App setup. Empty repos boots without the binding so a fresh
28
- // scaffold's `dev` / `eval` run; use `agent-sdk github replay` there.
25
+ // Empty `repos` skips the Cursor GitHub binding so `dev` / `eval` boot
26
+ // on a fresh scaffold.
29
27
  cursorAccount: config.repos.length > 0 ? { repos: config.repos } : undefined,
30
28
  // The model never writes to GitHub; record_review actuates host-side.
31
29
  deliverReplies: false,
@@ -33,9 +31,7 @@ export default githubChannel({
33
31
  log = (line) => args.logger(`[agentic-owners] ${line}`);
34
32
  },
35
33
  onPullRequest: async (ctx, pr) => {
36
- // A base retarget re-resolves the policy against the new base: the diff
37
- // and the governing policy files both changed, so the old verdict — and
38
- // any approval it produced — no longer covers this PR. The fresh
34
+ // A base retarget re-resolves policy against the new base. The fresh
39
35
  // actuation dismisses a leftover approve when the new decision holds.
40
36
  const baseRetargeted =
41
37
  pr.action === "edited" && pr.baseRefChangedFrom !== undefined;
@@ -54,9 +50,8 @@ export default githubChannel({
54
50
  try {
55
51
  const state = await loadReviewState(forge, log);
56
52
  if (!state.enrolled) {
57
- // Enrollment can end while an agent approval is live (base retarget,
58
- // owned files dropped); dismiss it instead of leaving it counting
59
- // toward branch protection under a policy that never granted it.
53
+ // Enrollment can end while an agent approval is live; dismiss it
54
+ // instead of leaving it counting toward branch protection.
60
55
  return {
61
56
  task: () =>
62
57
  dismissStaleApprovals(
@@ -69,14 +64,10 @@ export default githubChannel({
69
64
  auth: defaultGitHubAuth(ctx),
70
65
  message: renderReviewWake(state),
71
66
  title: `Review ${ctx.repository.fullName}#${pr.number}`,
72
- // Freeze the assessed head host-side: record_review reads this file
73
- // instead of trusting a model-supplied SHA, so a mid-turn push (or a
74
- // prompt-injected SHA) can never line up with the live head.
75
67
  workspaceFiles: { "pr/head.sha": state.pr.headSha },
76
68
  };
77
69
  } catch (error) {
78
70
  if (error instanceof AgenticOwnersConfigValidationError) {
79
- // Fail closed but visibly: comment the issues, review nothing.
80
71
  return { task: () => reportPolicyError(forge, error) };
81
72
  }
82
73
  throw error;
@@ -1,11 +1,3 @@
1
- /**
2
- * The one file to edit: which GitHub repositories the agent reviews.
3
- *
4
- * Setting it streams PR events and a repo-scoped GitHub credential through
5
- * your signed-in Cursor account (`agent-sdk login`) — no webhook or GitHub
6
- * App setup. While empty, the agent still runs locally against replayed
7
- * PRs (`agent-sdk github replay <pr-url>`).
8
- */
9
1
  const config = {
10
2
  repos: [] as string[], // e.g. ["acme/api"]
11
3
  };
@@ -1,8 +1,5 @@
1
- // Policy resolution and actuation over the GitHub API no repo checkout.
2
- // Every trusted input (policy files, rule prompts) is read from the PR's
3
- // **base ref**, so a PR cannot exempt itself by editing its own governance
4
- // files. Mirrors the Agentic Owners pipeline's `resolvePolicyForPr`, minus
5
- // the linguist-generated partition and content caching.
1
+ // Policy files and rule prompts are read from the PR's base ref so a PR
2
+ // cannot exempt itself by editing its own governance files.
6
3
 
7
4
  import {
8
5
  decideApproval,
@@ -297,12 +294,6 @@ export async function loadReviewState(
297
294
  };
298
295
  }
299
296
 
300
- /**
301
- * The model turn's wake message: composed rubric, PR context, and the
302
- * host-fetched diff. Fenced so author-controlled patch bytes cannot escape
303
- * into the trusted markdown (the fence outruns any backtick run inside the
304
- * patch).
305
- */
306
297
  /**
307
298
  * Filenames are author-controlled and render outside the diff fence: strip
308
299
  * control characters and backticks so a hostile path cannot fabricate
@@ -648,9 +639,6 @@ export async function actuateReview(input: {
648
639
  }).filter(
649
640
  (handle) => normalizeHandle(handle) !== normalizeHandle(pr.authorLogin ?? "")
650
641
  );
651
- // The full decision — policy verdict plus the governance / evidence
652
- // fail-closes — is computed in one place; from here on this function
653
- // only writes.
654
642
  const decision = decideApproval({
655
643
  matchedRules: state.matchedRules,
656
644
  riskLevel: verdict.riskLevel,
@@ -742,7 +730,6 @@ export async function actuateReview(input: {
742
730
  const alreadyRequested = new Set(
743
731
  pr.requestedReviewers.map(normalizeHandle)
744
732
  );
745
- // Adds fill only the remaining slots under the max-of-two rule.
746
733
  const newReviewers = decision.requestReviewers
747
734
  .filter((handle) => !alreadyRequested.has(normalizeHandle(handle)))
748
735
  .slice(
@@ -41,10 +41,8 @@ export default defineTool({
41
41
  "not a GitHub pull-request session — reviews start from the github channel (agent-sdk github replay <pr-url> to test locally)",
42
42
  };
43
43
  }
44
- // The channel wake froze the assessed head into the session workspace.
45
- // Reading it here (instead of accepting a model-supplied SHA) means a
46
- // mid-turn push can never line up with the live head: the actuation
47
- // compares wake-observed against live and skips on any mismatch.
44
+ // Wake froze the assessed head into the session workspace. A model-supplied
45
+ // SHA is never trusted.
48
46
  let assessedHeadSha: string;
49
47
  try {
50
48
  assessedHeadSha = (