@cursor/july 0.1.90 → 0.1.91

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 (266) hide show
  1. package/AGENTS.md +8 -0
  2. package/README.md +8 -7
  3. package/dist/channels/slack/channel-watch.d.ts +1 -1
  4. package/dist/channels/slack/channel-watch.js +1 -1
  5. package/dist/channels/slack/slack-channel.js +1 -1
  6. package/dist/channels/slack/types.d.ts +2 -4
  7. package/dist/channels/slack/types.d.ts.map +1 -1
  8. package/dist/docs/404.html +3 -3
  9. package/dist/docs/ab.html +7 -7
  10. package/dist/docs/assets/{ab.md.DYjwREAP.js → ab.md.CVzWxLoB.js} +1 -1
  11. package/dist/docs/assets/{ab.md.DYjwREAP.lean.js → ab.md.CVzWxLoB.lean.js} +1 -1
  12. package/dist/docs/assets/{app.wiNkt6G7.js → app.Bci6CM9E.js} +1 -1
  13. package/dist/docs/assets/{building-with-agents.md.PeZaZA1P.js → building-with-agents.md.DH8A_cHA.js} +1 -1
  14. package/dist/docs/assets/{building-with-agents.md.PeZaZA1P.lean.js → building-with-agents.md.DH8A_cHA.lean.js} +1 -1
  15. package/dist/docs/assets/chunks/@localSearchIndexroot.Dv-Q0XtU.js +1 -0
  16. package/dist/docs/assets/chunks/{VPLocalSearchBox.ByhUGu47.js → VPLocalSearchBox.BCPT6xA-.js} +1 -1
  17. package/dist/docs/assets/chunks/{framework.CAZyNGu9.js → framework.BCISBCiQ.js} +1 -1
  18. package/dist/docs/assets/chunks/theme.BEA8BF3c.js +2 -0
  19. package/dist/docs/assets/{concepts.md.2NfkGjqM.js → concepts.md.CRfU3bVg.js} +1 -1
  20. package/dist/docs/assets/{concepts.md.2NfkGjqM.lean.js → concepts.md.CRfU3bVg.lean.js} +1 -1
  21. package/dist/docs/assets/{deployment.md.B8kW-h7P.js → deployment.md.DX_hc3ze.js} +6 -5
  22. package/dist/docs/assets/{deployment.md.B8kW-h7P.lean.js → deployment.md.DX_hc3ze.lean.js} +1 -1
  23. package/dist/docs/assets/{evals.md.CVe_O75-.js → evals.md.a0SMN6r9.js} +3 -3
  24. package/dist/docs/assets/{evals.md.CVe_O75-.lean.js → evals.md.a0SMN6r9.lean.js} +1 -1
  25. package/dist/docs/assets/{example-agents_approval-buddy.md.CIiZ9coo.js → example-agents_approval-buddy.md.DNL83puR.js} +1 -1
  26. package/dist/docs/assets/{example-agents_approval-buddy.md.CIiZ9coo.lean.js → example-agents_approval-buddy.md.DNL83puR.lean.js} +1 -1
  27. package/dist/docs/assets/{example-agents_benny.md.B-LIDGja.js → example-agents_benny.md.C40vHRLc.js} +1 -1
  28. package/dist/docs/assets/{example-agents_benny.md.B-LIDGja.lean.js → example-agents_benny.md.C40vHRLc.lean.js} +1 -1
  29. package/dist/docs/assets/{example-agents_bugbot.md.Dp5JqHSQ.js → example-agents_bugbot.md.BRGMi9O2.js} +1 -1
  30. package/dist/docs/assets/{example-agents_bugbot.md.Dp5JqHSQ.lean.js → example-agents_bugbot.md.BRGMi9O2.lean.js} +1 -1
  31. package/dist/docs/assets/{example-agents_codebase-wiki.md.D-lteFf0.js → example-agents_codebase-wiki.md.Dftj_tPp.js} +1 -1
  32. package/dist/docs/assets/{example-agents_codebase-wiki.md.D-lteFf0.lean.js → example-agents_codebase-wiki.md.Dftj_tPp.lean.js} +1 -1
  33. package/dist/docs/assets/{example-agents_codeowners-review.md.BU2ZXLf-.js → example-agents_codeowners-review.md.Bfta-lBU.js} +1 -1
  34. package/dist/docs/assets/{example-agents_codeowners-review.md.BU2ZXLf-.lean.js → example-agents_codeowners-review.md.Bfta-lBU.lean.js} +1 -1
  35. package/dist/docs/assets/{example-agents_concierge.md.DA2al_NK.js → example-agents_concierge.md.MrKpQndp.js} +1 -1
  36. package/dist/docs/assets/{example-agents_concierge.md.DA2al_NK.lean.js → example-agents_concierge.md.MrKpQndp.lean.js} +1 -1
  37. package/dist/docs/assets/{example-agents_fsd.md.ZeGEpAw_.js → example-agents_fsd.md.ZWHWWZPE.js} +1 -1
  38. package/dist/docs/assets/{example-agents_fsd.md.ZeGEpAw_.lean.js → example-agents_fsd.md.ZWHWWZPE.lean.js} +1 -1
  39. package/dist/docs/assets/example-agents_index.md.QZ8mhr6n.js +2 -0
  40. package/dist/docs/assets/{example-agents_index.md.xmJ87d_3.lean.js → example-agents_index.md.QZ8mhr6n.lean.js} +1 -1
  41. package/dist/docs/assets/{example-agents_knowledge-base.md.IneynQSR.js → example-agents_knowledge-base.md.DqKqHQ9u.js} +1 -1
  42. package/dist/docs/assets/{example-agents_knowledge-base.md.IneynQSR.lean.js → example-agents_knowledge-base.md.DqKqHQ9u.lean.js} +1 -1
  43. package/dist/docs/assets/{example-agents_oncall.md.CBmyuAKc.js → example-agents_oncall.md.DK4XkYTd.js} +1 -1
  44. package/dist/docs/assets/{example-agents_oncall.md.CBmyuAKc.lean.js → example-agents_oncall.md.DK4XkYTd.lean.js} +1 -1
  45. package/dist/docs/assets/{example-agents_security-reviewer.md.DBL1TwtP.js → example-agents_security-reviewer.md.Bai6D0Ee.js} +4 -4
  46. package/dist/docs/assets/{example-agents_security-reviewer.md.DBL1TwtP.lean.js → example-agents_security-reviewer.md.Bai6D0Ee.lean.js} +1 -1
  47. package/dist/docs/assets/{example-agents_slack-agent.md.06jQXTAI.js → example-agents_slack-agent.md.D7Kdj5BV.js} +1 -1
  48. package/dist/docs/assets/{example-agents_slack-agent.md.06jQXTAI.lean.js → example-agents_slack-agent.md.D7Kdj5BV.lean.js} +1 -1
  49. package/dist/docs/assets/{example-agents_weather-agent.md.DC3lINjo.js → example-agents_weather-agent.md.lVEAbWFf.js} +5 -5
  50. package/dist/docs/assets/example-agents_weather-agent.md.lVEAbWFf.lean.js +1 -0
  51. package/dist/docs/assets/{guides_agent-to-agent.md.Bmbxy-FA.js → guides_agent-to-agent.md.BCeVdJRJ.js} +1 -1
  52. package/dist/docs/assets/{guides_agent-to-agent.md.Bmbxy-FA.lean.js → guides_agent-to-agent.md.BCeVdJRJ.lean.js} +1 -1
  53. package/dist/docs/assets/{guides_cloud-runtime.md.CbklWfzh.js → guides_cloud-runtime.md.BSMLIBHr.js} +1 -1
  54. package/dist/docs/assets/{guides_cloud-runtime.md.CbklWfzh.lean.js → guides_cloud-runtime.md.BSMLIBHr.lean.js} +1 -1
  55. package/dist/docs/assets/{guides_convert-automation.md.BhMzTkE5.js → guides_convert-automation.md.D06eIzea.js} +1 -1
  56. package/dist/docs/assets/{guides_convert-automation.md.BhMzTkE5.lean.js → guides_convert-automation.md.D06eIzea.lean.js} +1 -1
  57. package/dist/docs/assets/{guides_github.md.CLLQJKRB.js → guides_github.md.Cdt1s2QC.js} +4 -4
  58. package/dist/docs/assets/{guides_github.md.CLLQJKRB.lean.js → guides_github.md.Cdt1s2QC.lean.js} +1 -1
  59. package/dist/docs/assets/{guides_human-in-the-loop.md.Cf8kIIqC.js → guides_human-in-the-loop.md.By1G2T3_.js} +1 -1
  60. package/dist/docs/assets/{guides_human-in-the-loop.md.Cf8kIIqC.lean.js → guides_human-in-the-loop.md.By1G2T3_.lean.js} +1 -1
  61. package/dist/docs/assets/{guides_mcp-oauth.md.CzEB6RaG.js → guides_mcp-oauth.md.Du0f7pGU.js} +1 -1
  62. package/dist/docs/assets/{guides_mcp-oauth.md.CzEB6RaG.lean.js → guides_mcp-oauth.md.Du0f7pGU.lean.js} +1 -1
  63. package/dist/docs/assets/{guides_opentelemetry.md.Csn7ZI25.js → guides_opentelemetry.md.bmPmkvJu.js} +1 -1
  64. package/dist/docs/assets/{guides_opentelemetry.md.Csn7ZI25.lean.js → guides_opentelemetry.md.bmPmkvJu.lean.js} +1 -1
  65. package/dist/docs/assets/{guides_slack.md.DP4H75WP.js → guides_slack.md.DiUmk_Oi.js} +5 -5
  66. package/dist/docs/assets/{guides_slack.md.DP4H75WP.lean.js → guides_slack.md.DiUmk_Oi.lean.js} +1 -1
  67. package/dist/docs/assets/{guides_webhooks.md.DB-r_er9.js → guides_webhooks.md.BpnIdO0i.js} +5 -6
  68. package/dist/docs/assets/{guides_webhooks.md.DB-r_er9.lean.js → guides_webhooks.md.BpnIdO0i.lean.js} +1 -1
  69. package/dist/docs/assets/{hillclimbing.md.yXqdlv2R.js → hillclimbing.md.ywF3yDAd.js} +1 -1
  70. package/dist/docs/assets/{hillclimbing.md.yXqdlv2R.lean.js → hillclimbing.md.ywF3yDAd.lean.js} +1 -1
  71. package/dist/docs/assets/index.md.BAaMXLFd.js +5 -0
  72. package/dist/docs/assets/{index.md.DhRHS_-L.lean.js → index.md.BAaMXLFd.lean.js} +1 -1
  73. package/dist/docs/assets/{quickstart.md.DZxBu44y.js → quickstart.md.DsrarzEg.js} +1 -1
  74. package/dist/docs/assets/{quickstart.md.DZxBu44y.lean.js → quickstart.md.DsrarzEg.lean.js} +1 -1
  75. package/dist/docs/assets/{reference_agent-config.md.CTWp4DnU.js → reference_agent-config.md.Bqylgw50.js} +1 -1
  76. package/dist/docs/assets/{reference_agent-config.md.CTWp4DnU.lean.js → reference_agent-config.md.Bqylgw50.lean.js} +1 -1
  77. package/dist/docs/assets/{reference_artifacts.md.BGG4bZo-.js → reference_artifacts.md.Dior32Qw.js} +1 -1
  78. package/dist/docs/assets/{reference_artifacts.md.BGG4bZo-.lean.js → reference_artifacts.md.Dior32Qw.lean.js} +1 -1
  79. package/dist/docs/assets/{reference_channels.md.CboFd5IH.js → reference_channels.md.DQZjCnyh.js} +2 -2
  80. package/dist/docs/assets/{reference_channels.md.CboFd5IH.lean.js → reference_channels.md.DQZjCnyh.lean.js} +1 -1
  81. package/dist/docs/assets/{reference_cli.md.TAaYU8br.js → reference_cli.md.B7GkAJRC.js} +8 -7
  82. package/dist/docs/assets/{reference_cli.md.TAaYU8br.lean.js → reference_cli.md.B7GkAJRC.lean.js} +1 -1
  83. package/dist/docs/assets/{reference_connections.md.Cu3N-S3Q.js → reference_connections.md.DYidrb-j.js} +5 -5
  84. package/dist/docs/assets/{reference_connections.md.Cu3N-S3Q.lean.js → reference_connections.md.DYidrb-j.lean.js} +1 -1
  85. package/dist/docs/assets/{reference_hooks.md.DJE5DXcT.js → reference_hooks.md.B9FSgdDe.js} +1 -1
  86. package/dist/docs/assets/{reference_hooks.md.DJE5DXcT.lean.js → reference_hooks.md.B9FSgdDe.lean.js} +1 -1
  87. package/dist/docs/assets/{reference_http-api.md.DMbdFGVQ.js → reference_http-api.md.CSHVobzG.js} +4 -4
  88. package/dist/docs/assets/{reference_http-api.md.DMbdFGVQ.lean.js → reference_http-api.md.CSHVobzG.lean.js} +1 -1
  89. package/dist/docs/assets/{reference_instructions.md.CgoV-YEb.js → reference_instructions.md.DhNCOl7r.js} +1 -1
  90. package/dist/docs/assets/{reference_instructions.md.CgoV-YEb.lean.js → reference_instructions.md.DhNCOl7r.lean.js} +1 -1
  91. package/dist/docs/assets/{reference_playground.md.CPZhfYaO.js → reference_playground.md.Dfb92yQf.js} +1 -1
  92. package/dist/docs/assets/{reference_playground.md.CPZhfYaO.lean.js → reference_playground.md.Dfb92yQf.lean.js} +1 -1
  93. package/dist/docs/assets/{reference_project-layout.md.CueaKpjr.js → reference_project-layout.md.CwkSbEWT.js} +1 -1
  94. package/dist/docs/assets/{reference_project-layout.md.CueaKpjr.lean.js → reference_project-layout.md.CwkSbEWT.lean.js} +1 -1
  95. package/dist/docs/assets/{reference_prompt.md.BaiweQxE.js → reference_prompt.md.DZUMtLPD.js} +1 -1
  96. package/dist/docs/assets/{reference_prompt.md.BaiweQxE.lean.js → reference_prompt.md.DZUMtLPD.lean.js} +1 -1
  97. package/dist/docs/assets/{reference_schedules.md.gmfYzf_I.js → reference_schedules.md.DNipebiG.js} +1 -1
  98. package/dist/docs/assets/{reference_schedules.md.gmfYzf_I.lean.js → reference_schedules.md.DNipebiG.lean.js} +1 -1
  99. package/dist/docs/assets/{reference_sessions.md.B0DdlM-K.js → reference_sessions.md.tUFzz98S.js} +1 -1
  100. package/dist/docs/assets/{reference_sessions.md.B0DdlM-K.lean.js → reference_sessions.md.tUFzz98S.lean.js} +1 -1
  101. package/dist/docs/assets/{reference_skills.md.BRF2nDv9.js → reference_skills.md.B5ZEuHfG.js} +1 -1
  102. package/dist/docs/assets/{reference_skills.md.BRF2nDv9.lean.js → reference_skills.md.B5ZEuHfG.lean.js} +1 -1
  103. package/dist/docs/assets/{reference_subagents.md.DSrGLIuB.js → reference_subagents.md.Xoav0AII.js} +1 -1
  104. package/dist/docs/assets/{reference_subagents.md.DSrGLIuB.lean.js → reference_subagents.md.Xoav0AII.lean.js} +1 -1
  105. package/dist/docs/assets/{reference_tools.md.XmeFP_3d.js → reference_tools.md.wpaJtHn6.js} +2 -3
  106. package/dist/docs/assets/{reference_tools.md.XmeFP_3d.lean.js → reference_tools.md.wpaJtHn6.lean.js} +1 -1
  107. package/dist/docs/assets/{scaffolding-agents.md.BpMFXv2J.js → scaffolding-agents.md.CRDDUtYJ.js} +1 -1
  108. package/dist/docs/assets/{scaffolding-agents.md.BpMFXv2J.lean.js → scaffolding-agents.md.CRDDUtYJ.lean.js} +1 -1
  109. package/dist/docs/assets/{storage.md.ks1u64_R.js → storage.md.JbjlHWZ6.js} +1 -1
  110. package/dist/docs/assets/{storage.md.ks1u64_R.lean.js → storage.md.JbjlHWZ6.lean.js} +1 -1
  111. package/dist/docs/assets/{style.kTsvp4pE.css → style.BRuM8477.css} +1 -1
  112. package/dist/docs/assets/{templates_agentic-owners.md.BkTLORaU.js → templates_agentic-owners.md.DSJSIpWU.js} +1 -1
  113. package/dist/docs/assets/{templates_agentic-owners.md.BkTLORaU.lean.js → templates_agentic-owners.md.DSJSIpWU.lean.js} +1 -1
  114. package/dist/docs/assets/{templates_demo.md.Bgd6MBaZ.js → templates_demo.md.DhFcWN6j.js} +1 -1
  115. package/dist/docs/assets/{templates_demo.md.Bgd6MBaZ.lean.js → templates_demo.md.DhFcWN6j.lean.js} +1 -1
  116. package/dist/docs/assets/{templates_pr-autofixer.md.DcmoeUNZ.js → templates_pr-autofixer.md.1HAR3RXE.js} +1 -1
  117. package/dist/docs/assets/{templates_pr-autofixer.md.DcmoeUNZ.lean.js → templates_pr-autofixer.md.1HAR3RXE.lean.js} +1 -1
  118. package/dist/docs/assets/{templates_security-reviewer.md.C0yIUaYs.js → templates_security-reviewer.md.ByFyRta2.js} +1 -1
  119. package/dist/docs/assets/{templates_security-reviewer.md.C0yIUaYs.lean.js → templates_security-reviewer.md.ByFyRta2.lean.js} +1 -1
  120. package/dist/docs/assets/{templates_triage.md.DWuQ1bZz.js → templates_triage.md.CVlpctKS.js} +2 -2
  121. package/dist/docs/assets/{templates_triage.md.DWuQ1bZz.lean.js → templates_triage.md.CVlpctKS.lean.js} +1 -1
  122. package/dist/docs/assets/troubleshooting.md.DYECCZiJ.js +1 -0
  123. package/dist/docs/assets/{troubleshooting.md.KgmmaCgw.lean.js → troubleshooting.md.DYECCZiJ.lean.js} +1 -1
  124. package/dist/docs/building-with-agents.html +7 -7
  125. package/dist/docs/concepts.html +7 -7
  126. package/dist/docs/deployment.html +11 -10
  127. package/dist/docs/evals.html +8 -8
  128. package/dist/docs/example-agents/approval-buddy.html +7 -7
  129. package/dist/docs/example-agents/benny.html +7 -7
  130. package/dist/docs/example-agents/bugbot.html +7 -7
  131. package/dist/docs/example-agents/codebase-wiki.html +7 -7
  132. package/dist/docs/example-agents/codeowners-review.html +7 -7
  133. package/dist/docs/example-agents/concierge.html +7 -7
  134. package/dist/docs/example-agents/fsd.html +7 -7
  135. package/dist/docs/example-agents/index.html +7 -7
  136. package/dist/docs/example-agents/knowledge-base.html +7 -7
  137. package/dist/docs/example-agents/oncall.html +7 -7
  138. package/dist/docs/example-agents/security-reviewer.html +10 -10
  139. package/dist/docs/example-agents/slack-agent.html +7 -7
  140. package/dist/docs/example-agents/weather-agent.html +12 -12
  141. package/dist/docs/guides/agent-to-agent.html +7 -7
  142. package/dist/docs/guides/cloud-runtime.html +7 -7
  143. package/dist/docs/guides/convert-automation.html +7 -7
  144. package/dist/docs/guides/github.html +9 -9
  145. package/dist/docs/guides/human-in-the-loop.html +7 -7
  146. package/dist/docs/guides/mcp-oauth.html +7 -7
  147. package/dist/docs/guides/opentelemetry.html +7 -7
  148. package/dist/docs/guides/slack.html +10 -10
  149. package/dist/docs/guides/webhooks.html +10 -11
  150. package/dist/docs/hashmap.json +1 -1
  151. package/dist/docs/hillclimbing.html +7 -7
  152. package/dist/docs/index.html +8 -8
  153. package/dist/docs/quickstart.html +7 -7
  154. package/dist/docs/reference/agent-config.html +7 -7
  155. package/dist/docs/reference/artifacts.html +7 -7
  156. package/dist/docs/reference/channels.html +8 -8
  157. package/dist/docs/reference/cli.html +13 -12
  158. package/dist/docs/reference/connections.html +10 -10
  159. package/dist/docs/reference/hooks.html +7 -7
  160. package/dist/docs/reference/http-api.html +10 -10
  161. package/dist/docs/reference/instructions.html +7 -7
  162. package/dist/docs/reference/playground.html +7 -7
  163. package/dist/docs/reference/project-layout.html +7 -7
  164. package/dist/docs/reference/prompt.html +7 -7
  165. package/dist/docs/reference/schedules.html +7 -7
  166. package/dist/docs/reference/sessions.html +7 -7
  167. package/dist/docs/reference/skills.html +7 -7
  168. package/dist/docs/reference/subagents.html +7 -7
  169. package/dist/docs/reference/tools.html +8 -9
  170. package/dist/docs/scaffolding-agents.html +7 -7
  171. package/dist/docs/storage.html +7 -7
  172. package/dist/docs/templates/agentic-owners.html +7 -7
  173. package/dist/docs/templates/demo.html +7 -7
  174. package/dist/docs/templates/pr-autofixer.html +7 -7
  175. package/dist/docs/templates/security-reviewer.html +7 -7
  176. package/dist/docs/templates/triage.html +8 -8
  177. package/dist/docs/troubleshooting.html +7 -7
  178. package/dist/internal/cli-deploy.d.ts.map +1 -1
  179. package/dist/internal/cli-deploy.js +21 -3
  180. package/dist/internal/cursor-agent-template.d.ts +1 -1
  181. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  182. package/dist/internal/cursor-agent-template.js +1 -0
  183. package/dist/internal/deploy-client.d.ts +18 -0
  184. package/dist/internal/deploy-client.d.ts.map +1 -1
  185. package/dist/internal/deploy-client.js +25 -1
  186. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  187. package/dist/internal/deploy-manifest.js +5 -1
  188. package/dist/internal/discovery.d.ts +5 -1
  189. package/dist/internal/discovery.d.ts.map +1 -1
  190. package/dist/internal/discovery.js +16 -0
  191. package/dist/internal/http-channel.d.ts +2 -2
  192. package/dist/internal/http-channel.d.ts.map +1 -1
  193. package/dist/internal/http-channel.js +11 -31
  194. package/dist/internal/init-project.d.ts.map +1 -1
  195. package/dist/internal/init-project.js +2 -0
  196. package/dist/internal/session-engine.d.ts +8 -0
  197. package/dist/internal/session-engine.d.ts.map +1 -1
  198. package/dist/internal/session-engine.js +47 -5
  199. package/dist/playground/assets/index-D9N7-q97.css +1 -0
  200. package/dist/playground/assets/{index-DxapiDj_.js → index-DDvyC2z6.js} +49 -49
  201. package/dist/playground/index.html +2 -2
  202. package/dist/types.d.ts +8 -5
  203. package/dist/types.d.ts.map +1 -1
  204. package/docs/README.md +3 -1
  205. package/docs/concepts.md +1 -1
  206. package/docs/deployment.md +9 -6
  207. package/docs/evals.md +1 -5
  208. package/docs/example-agents/codebase-wiki.md +2 -3
  209. package/docs/example-agents/fsd.md +1 -1
  210. package/docs/example-agents/index.md +2 -2
  211. package/docs/example-agents/knowledge-base.md +3 -3
  212. package/docs/example-agents/security-reviewer.md +17 -48
  213. package/docs/example-agents/weather-agent.md +14 -45
  214. package/docs/guides/github.md +7 -4
  215. package/docs/guides/slack.md +7 -2
  216. package/docs/guides/webhooks.md +12 -15
  217. package/docs/reference/channels.md +4 -0
  218. package/docs/reference/cli.md +21 -7
  219. package/docs/reference/connections.md +13 -32
  220. package/docs/reference/http-api.md +14 -9
  221. package/docs/reference/tools.md +1 -3
  222. package/docs/templates/triage.md +3 -5
  223. package/docs/troubleshooting.md +1 -1
  224. package/package.json +1 -1
  225. package/skills/create-agent/SKILL.md +6 -7
  226. package/skills/framework-map/SKILL.md +6 -6
  227. package/skills/setup-slack/SKILL.md +1 -1
  228. package/src/channels/slack/channel-watch.ts +1 -1
  229. package/src/channels/slack/slack-channel.ts +1 -1
  230. package/src/channels/slack/types.ts +2 -4
  231. package/src/internal/cli-deploy.ts +27 -7
  232. package/src/internal/cursor-agent-template.ts +1 -0
  233. package/src/internal/deploy-client.ts +40 -1
  234. package/src/internal/deploy-manifest.ts +10 -1
  235. package/src/internal/discovery.ts +20 -0
  236. package/src/internal/http-channel.ts +14 -41
  237. package/src/internal/init-project.ts +2 -0
  238. package/src/internal/session-engine.ts +58 -8
  239. package/src/types.ts +8 -5
  240. package/templates/security-help/README.md +37 -0
  241. package/templates/security-help/agent/agent.ts +18 -0
  242. package/templates/security-help/agent/channels/github.ts +13 -0
  243. package/templates/security-help/agent/channels/slack.ts +89 -0
  244. package/templates/security-help/agent/instructions.md +20 -0
  245. package/templates/security-help/agent/knowledge/faq/approvals.md +5 -0
  246. package/templates/security-help/agent/knowledge/faq/channels.md +6 -0
  247. package/templates/security-help/agent/knowledge/faq/phishing.md +10 -0
  248. package/templates/security-help/agent/lib/pr-first-pass.ts +42 -0
  249. package/templates/security-help/agent/lib/repos.ts +5 -0
  250. package/templates/security-help/agent/skills/access-request.md +10 -0
  251. package/templates/security-help/agent/skills/security-first-pass.md +15 -0
  252. package/templates/security-help/agent/skills/security-playbooks.md +12 -0
  253. package/templates/security-help/agent/skills/security-pr-first-pass.md +12 -0
  254. package/templates/security-help/evals/evals.config.ts +5 -0
  255. package/templates/security-help/evals/security-help.eval.ts +53 -0
  256. package/templates/security-help/init.json +31 -0
  257. package/templates/security-help/package.json +18 -0
  258. package/templates/security-help/tsconfig.json +12 -0
  259. package/templates/triage/agent/channels/webhook.ts +2 -2
  260. package/dist/docs/assets/chunks/@localSearchIndexroot.FV0R6kOb.js +0 -1
  261. package/dist/docs/assets/chunks/theme.Dx7j_-0n.js +0 -2
  262. package/dist/docs/assets/example-agents_index.md.xmJ87d_3.js +0 -2
  263. package/dist/docs/assets/example-agents_weather-agent.md.DC3lINjo.lean.js +0 -1
  264. package/dist/docs/assets/index.md.DhRHS_-L.js +0 -5
  265. package/dist/docs/assets/troubleshooting.md.KgmmaCgw.js +0 -1
  266. package/dist/playground/assets/index-BpVS-paP.css +0 -1
@@ -28,6 +28,7 @@ import { readDeployTemplate } from "./cursor-agent-template.js";
28
28
  import {
29
29
  type AgentkitDeployAccepted,
30
30
  type AgentkitDeployStatus,
31
+ type AgentkitStopAccepted,
31
32
  AgentServeDeployClient,
32
33
  type DeleteAccepted,
33
34
  type DeployAccepted,
@@ -1187,11 +1188,25 @@ export async function cmdStop(
1187
1188
  if (typeof ctx === "number") {
1188
1189
  return ctx;
1189
1190
  }
1190
- let accepted: StopAccepted;
1191
+ // Stop takes only a slug, so there is no manifest to dispatch on (unlike
1192
+ // deploy). The v2 agentkit control plane goes first: v1 endpoints refuse
1193
+ // `architecture: "v2"` slugs with a 409 rather than a 404, so v1-first
1194
+ // could not fall through. A v2 404 (including an older backend without
1195
+ // the agentkit mount) falls back to the v1 stop.
1196
+ let architecture: "v1" | "v2" = "v2";
1197
+ let accepted: StopAccepted | AgentkitStopAccepted;
1191
1198
  try {
1192
- accepted = await ctx.client.stopDeployment({ teamId: ctx.teamId, slug });
1199
+ accepted = await ctx.client.stopAgentkit({ teamId: ctx.teamId, slug });
1193
1200
  } catch (error) {
1194
- return renderApiError(error, ctx.err);
1201
+ if (!(error instanceof DeployApiError) || error.status !== 404) {
1202
+ return renderApiError(error, ctx.err);
1203
+ }
1204
+ try {
1205
+ accepted = await ctx.client.stopDeployment({ teamId: ctx.teamId, slug });
1206
+ architecture = "v1";
1207
+ } catch (v1Error) {
1208
+ return renderApiError(v1Error, ctx.err);
1209
+ }
1195
1210
  }
1196
1211
  if (ctx.noWait) {
1197
1212
  if (ctx.json) {
@@ -1201,11 +1216,16 @@ export async function cmdStop(
1201
1216
  }
1202
1217
  return 0;
1203
1218
  }
1204
- let detail: DeploymentDetail;
1219
+ let detail: DeploymentDetail | AgentkitDeployStatus;
1205
1220
  try {
1206
- detail = await pollUntil(ctx, slug, new Set(["stopped"]), () =>
1207
- ctx.client.getDeployment({ teamId: ctx.teamId, slug })
1208
- );
1221
+ detail =
1222
+ architecture === "v2"
1223
+ ? await pollUntil(ctx, slug, new Set(["stopped"]), () =>
1224
+ ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug })
1225
+ )
1226
+ : await pollUntil(ctx, slug, new Set(["stopped"]), () =>
1227
+ ctx.client.getDeployment({ teamId: ctx.teamId, slug })
1228
+ );
1209
1229
  } catch (error) {
1210
1230
  return renderApiError(error, ctx.err);
1211
1231
  }
@@ -6,6 +6,7 @@ const TEMPLATE_NAMES = [
6
6
  "agentic-owners",
7
7
  "demo",
8
8
  "pr-autofixer",
9
+ "security-help",
9
10
  "security-reviewer",
10
11
  "triage-linear",
11
12
  "triage-jira",
@@ -255,6 +255,17 @@ export interface AgentkitDeployStatus {
255
255
  export const TERMINAL_AGENTKIT_DEPLOY_STATUSES: ReadonlySet<AgentkitCustomerStatus> =
256
256
  new Set(["running", "failed", "stopped"]);
257
257
 
258
+ /**
259
+ * `POST /internal/agentkit/deployments/:slug/stop` 202 body. Like v1
260
+ * {@link StopAccepted} but keyed by catalog ids instead of the numeric
261
+ * deployment row id.
262
+ */
263
+ export interface AgentkitStopAccepted {
264
+ applicationId: string;
265
+ releaseId: string;
266
+ status: string;
267
+ }
268
+
258
269
  /** One secret's metadata from the list endpoint — names only, never values. */
259
270
  export interface SecretInfo {
260
271
  name: string;
@@ -332,6 +343,14 @@ const agentkitDeployStatusSchema = z
332
343
  })
333
344
  .passthrough();
334
345
 
346
+ const agentkitStopAcceptedSchema = z
347
+ .object({
348
+ applicationId: z.string().min(1),
349
+ releaseId: z.string().min(1),
350
+ status: z.string(),
351
+ })
352
+ .passthrough();
353
+
335
354
  const deploymentSchema = z.object({
336
355
  id: z.number(),
337
356
  slug: z.string(),
@@ -602,6 +621,22 @@ export class AgentServeDeployClient {
602
621
  return agentkitDeployStatusSchema.parse(raw);
603
622
  }
604
623
 
624
+ /**
625
+ * Stop a v2 deployment (`POST /internal/agentkit/deployments/:slug/stop`).
626
+ * 202 means accepted; poll {@link getAgentkitDeploy} until `stopped`.
627
+ */
628
+ async stopAgentkit(args: {
629
+ teamId: number;
630
+ slug: string;
631
+ }): Promise<AgentkitStopAccepted> {
632
+ const raw = await this.request({
633
+ method: "POST",
634
+ path: `/internal/agentkit/deployments/${encodeURIComponent(args.slug)}/stop?teamId=${args.teamId}`,
635
+ context: { verb: "stop", slug: args.slug },
636
+ });
637
+ return agentkitStopAcceptedSchema.parse(raw);
638
+ }
639
+
605
640
  async listDeployments(args: { teamId: number }): Promise<Deployment[]> {
606
641
  const raw = await this.request({
607
642
  method: "GET",
@@ -888,8 +923,12 @@ function mapErrorResponse(
888
923
  );
889
924
  }
890
925
  case 409:
926
+ // The server's 409 body distinguishes real conflicts (CAS races,
927
+ // "still deploying", architecture-v2 slugs on the v1 surface); keep
928
+ // its message when present.
891
929
  return new DeployApiError(
892
- `A concurrent deploy or stop is changing deployment${slugPart}; retry in a moment.`,
930
+ serverError ??
931
+ `A concurrent deploy or stop is changing deployment${slugPart}; retry in a moment.`,
893
932
  409
894
933
  );
895
934
  case 429: {
@@ -40,7 +40,10 @@ import {
40
40
  handshakeChannelContinuation,
41
41
  } from "./continuation-channel.js";
42
42
  import { CONTINUATION_SELECTORS } from "./continuation-identity.js";
43
- import { projectInfo } from "./discovery.js";
43
+ import {
44
+ projectInfo,
45
+ runtimeArchitectureCompatibilityError,
46
+ } from "./discovery.js";
44
47
  import { packageVersion } from "./distribution.js";
45
48
 
46
49
  export const DEPLOY_MANIFEST_VERSION: 1 = 1;
@@ -125,6 +128,12 @@ export function buildAgentDeployManifest(
125
128
  project: AgentProject,
126
129
  options?: { nowMs?: number; cliVersion?: string }
127
130
  ): AgentDeployManifestV1 {
131
+ const runtimeArchitectureError = runtimeArchitectureCompatibilityError(
132
+ project.agent
133
+ );
134
+ if (runtimeArchitectureError !== undefined) {
135
+ throw new Error(runtimeArchitectureError);
136
+ }
128
137
  applyAgentContinuationDefaults(project);
129
138
  const channels = project.channels.map((channel) => {
130
139
  const handshake = handshakeChannelContinuation(channel);
@@ -524,6 +524,17 @@ async function loadAgent(
524
524
  const hosting = resolveHosting(ctx, agentDir, config?.hosting);
525
525
  const concurrency = resolveConcurrency(ctx, agentDir, config?.concurrency);
526
526
  const architecture = resolveArchitecture(ctx, agentDir, config?.architecture);
527
+ const runtimeArchitectureError = runtimeArchitectureCompatibilityError({
528
+ runtime,
529
+ architecture,
530
+ });
531
+ if (runtimeArchitectureError !== undefined) {
532
+ ctx.diagnostics.push({
533
+ severity: "error",
534
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
535
+ message: runtimeArchitectureError,
536
+ });
537
+ }
527
538
 
528
539
  if (runtime === "cloud") {
529
540
  warnCloudRuntimeLimitations(ctx, agentDir, {
@@ -570,6 +581,15 @@ async function loadAgent(
570
581
  };
571
582
  }
572
583
 
584
+ export function runtimeArchitectureCompatibilityError(agent: {
585
+ runtime: AgentRuntime;
586
+ architecture?: AgentArchitecture;
587
+ }): string | undefined {
588
+ return agent.architecture === "v2" && agent.runtime === "grokbot"
589
+ ? 'architecture: "v2" cannot use runtime: "grokbot": the grokbot runtime executes through the signed-in account\'s hosted /v0/grokbot box, not a deployment-scoped v2 runtime.'
590
+ : undefined;
591
+ }
592
+
573
593
  function resolveArchitecture(
574
594
  ctx: DiscoveryContext,
575
595
  agentDir: string,
@@ -7,8 +7,8 @@
7
7
  * Continuation semantics: `POST /v1/session` returns a `continuationToken`;
8
8
  * each accepted follow-up rotates it and returns the replacement, and a
9
9
  * stale token is rejected with 409. Follow-ups address any chat session by
10
- * id (including custom channels such as `drive`) when the token matches
11
- * that session's channel + key; task sessions stay fire-and-forget.
10
+ * id (including custom channels such as `drive`) when the token matches its
11
+ * active HTTP follow-up key; task sessions stay fire-and-forget.
12
12
  */
13
13
 
14
14
  import type {
@@ -20,11 +20,7 @@ import type {
20
20
  SessionRecord,
21
21
  } from "../types.js";
22
22
  import { samePrincipal } from "./auth.js";
23
- import {
24
- formatContinuationToken,
25
- newContinuationKey,
26
- parseContinuationToken,
27
- } from "./ids.js";
23
+ import { formatContinuationToken, parseContinuationToken } from "./ids.js";
28
24
  import { isLoopbackAddress } from "./net.js";
29
25
  import type { ServerRoute } from "./router.js";
30
26
  import {
@@ -32,7 +28,6 @@ import {
32
28
  ApprovalNotFoundError,
33
29
  HTTP_CHANNEL_ID,
34
30
  SessionAuthError,
35
- SessionBusyError,
36
31
  type SessionEngine,
37
32
  } from "./session-engine.js";
38
33
  import {
@@ -143,10 +138,8 @@ export function buildHttpChannelRoutes(args: {
143
138
  `Unknown session ${sessionId}.`
144
139
  );
145
140
  }
146
- // Playground (and other HTTP clients) may follow up on any chat
147
- // session including custom channels like `drive` — using that
148
- // session's own continuation token. Task/schedule sessions stay
149
- // fire-and-forget.
141
+ // HTTP can follow any chat session. Its credential is separate from the
142
+ // owning channel's continuation key.
150
143
  if (record.mode === "task") {
151
144
  return errorResponse(
152
145
  409,
@@ -178,12 +171,7 @@ export function buildHttpChannelRoutes(args: {
178
171
  );
179
172
  }
180
173
  const presented = parseContinuationToken(body.continuationToken);
181
- if (
182
- presented === null ||
183
- presented.channelId !== record.channelId ||
184
- record.continuationKey === null ||
185
- presented.key !== record.continuationKey
186
- ) {
174
+ if (presented === null) {
187
175
  return errorResponse(
188
176
  409,
189
177
  "stale_continuation_token",
@@ -192,34 +180,19 @@ export function buildHttpChannelRoutes(args: {
192
180
  }
193
181
 
194
182
  const message = await transformMessage(body.message, ctx.auth);
195
- try {
196
- // Continue as the session owner after the HTTP ownership gate so
197
- // `--dev` relaxed reads can follow up on channel-owned sessions
198
- // (drive/slack/…) without failing SessionAuthError.
199
- await engine.send(record.channelId, message, {
200
- auth: record.auth,
201
- continuationToken: record.continuationKey,
202
- });
203
- } catch (error) {
204
- if (error instanceof SessionBusyError) {
205
- return errorResponse(
206
- 409,
207
- "session_busy",
208
- "A turn is already running; wait for session.waiting before sending a follow-up."
209
- );
210
- }
211
- throw error;
183
+ const rotated = await engine.followUpHttp(sessionId, presented, message);
184
+ if (rotated === undefined) {
185
+ return errorResponse(
186
+ 409,
187
+ "stale_continuation_token",
188
+ "The continuation token does not address this session's active continuation."
189
+ );
212
190
  }
213
-
214
- // Rotate only after the message is accepted so a rejected send leaves
215
- // the caller's token valid for retry.
216
- const rotated = newContinuationKey();
217
- await engine.setContinuationToken(sessionId, rotated);
218
191
  return jsonResponse(
219
192
  {
220
193
  ok: true,
221
194
  sessionId,
222
- continuationToken: formatContinuationToken(record.channelId, rotated),
195
+ continuationToken: formatContinuationToken(HTTP_CHANNEL_ID, rotated),
223
196
  },
224
197
  { headers: { [SESSION_ID_HEADER]: sessionId } }
225
198
  );
@@ -76,6 +76,7 @@ const TEMPLATES: Readonly<Record<TemplateName, TemplateEntry>> = {
76
76
  "agentic-owners": { dir: "agentic-owners" },
77
77
  demo: { dir: "demo" },
78
78
  "pr-autofixer": { dir: "pr-autofixer" },
79
+ "security-help": { dir: "security-help" },
79
80
  "security-reviewer": { dir: "security-reviewer" },
80
81
  "triage-linear": { dir: "triage", overlay: "linear" },
81
82
  "triage-jira": { dir: "triage", overlay: "jira" },
@@ -709,6 +710,7 @@ Scaffold a project, then open the playground:
709
710
  npx ${PACKAGE_NAME} init ./my-agent # installs deps + links agent-sdk onto PATH
710
711
  npx ${PACKAGE_NAME} init ./my-demo --template demo # record a PR walkthrough
711
712
  npx ${PACKAGE_NAME} init ./my-reviewer --template security-reviewer # review PRs for security bugs
713
+ npx ${PACKAGE_NAME} init ./security-help --template security-help # answer security questions in Slack
712
714
  npx ${PACKAGE_NAME} init ./my-triage --template triage-linear # comment on Linear issues
713
715
  npx ${PACKAGE_NAME} init ./my-triage --template triage-jira # comment on Jira issues
714
716
  npx ${PACKAGE_NAME} init ./my-owners --template agentic-owners # review PRs via owners policies
@@ -925,6 +925,51 @@ export class SessionEngine {
925
925
  return this.channelSession(record.sessionId, record, true, false, since);
926
926
  }
927
927
 
928
+ /**
929
+ * Atomically validate and rotate an HTTP follow-up credential, then admit
930
+ * the message to its exact session without changing the owning channel key.
931
+ */
932
+ async followUpHttp(
933
+ sessionId: string,
934
+ token: { channelId: string; key: string },
935
+ message: string
936
+ ): Promise<string | undefined> {
937
+ const next = newContinuationKey();
938
+ const stale = new Error("stale HTTP continuation");
939
+ let previous: string | undefined;
940
+ try {
941
+ await this.updateRecord(sessionId, (record) => {
942
+ const expectedChannel =
943
+ record.httpContinuationKey === undefined
944
+ ? record.channelId
945
+ : HTTP_CHANNEL_ID;
946
+ const expectedKey =
947
+ record.httpContinuationKey ?? record.continuationKey;
948
+ if (token.channelId !== expectedChannel || token.key !== expectedKey) {
949
+ throw stale;
950
+ }
951
+ previous = record.httpContinuationKey;
952
+ return { ...record, httpContinuationKey: next };
953
+ });
954
+ } catch (error) {
955
+ if (error === stale) {
956
+ return undefined;
957
+ }
958
+ throw error;
959
+ }
960
+ try {
961
+ await this.startTurn(sessionId, message);
962
+ return next;
963
+ } catch (error) {
964
+ await this.updateRecord(sessionId, (record) =>
965
+ record.httpContinuationKey === next
966
+ ? { ...record, httpContinuationKey: previous }
967
+ : record
968
+ );
969
+ throw error;
970
+ }
971
+ }
972
+
928
973
  /** Whether a continuation token already maps to a session on this channel. */
929
974
  async hasContinuationSession(
930
975
  channelId: string,
@@ -3315,12 +3360,17 @@ export class SessionEngine {
3315
3360
  createdAt: record.createdAt,
3316
3361
  updatedAt: record.updatedAt,
3317
3362
  continuationToken:
3318
- record.continuationKey === null
3319
- ? null
3320
- : formatContinuationToken(
3321
- record.channelId,
3322
- record.continuationKey
3323
- ),
3363
+ record.httpContinuationKey !== undefined
3364
+ ? formatContinuationToken(
3365
+ HTTP_CHANNEL_ID,
3366
+ record.httpContinuationKey
3367
+ )
3368
+ : record.continuationKey === null
3369
+ ? null
3370
+ : formatContinuationToken(
3371
+ record.channelId,
3372
+ record.continuationKey
3373
+ ),
3324
3374
  externalUrl: links.slack ?? links.github,
3325
3375
  prUrl: links.github,
3326
3376
  };
@@ -3606,8 +3656,8 @@ export class SessionEngine {
3606
3656
  ...current,
3607
3657
  continuationKey: token,
3608
3658
  }));
3609
- // Keep armed reminders addressed at the conversation across rotation
3610
- // (the built-in HTTP channel rotates after every follow-up).
3659
+ // Keep armed reminders addressed at the conversation across channel
3660
+ // continuation rotation.
3611
3661
  if (
3612
3662
  previous?.continuationKey != null &&
3613
3663
  token !== null &&
package/src/types.ts CHANGED
@@ -1201,10 +1201,8 @@ export type ConnectionConfig = (
1201
1201
  *
1202
1202
  * Derive the identity from durable session facts — `session.auth`,
1203
1203
  * `session.id`, or your channel's own session state. Do **not** key it
1204
- * off `session.continuationKey`: the HTTP channel rotates the
1205
- * continuation key after every accepted follow-up, so a tenant mapping
1206
- * keyed on it silently breaks mid-conversation. (Channels that mint
1207
- * stable, parseable tokens by design are the exception.)
1204
+ * off `session.continuationKey`: it is a channel routing key, and channels
1205
+ * may re-key it independently of the authenticated principal.
1208
1206
  *
1209
1207
  * Works attached or advertised. Advertised connections
1210
1208
  * ({@link advertiseTools}) open per-operation clients with the
@@ -1593,6 +1591,11 @@ export interface SessionRecord {
1593
1591
  abs?: Record<string, string | null>;
1594
1592
  /** Channel-local continuation key; null when the session is not addressable. */
1595
1593
  continuationKey: string | null;
1594
+ /**
1595
+ * Rotating HTTP follow-up credential for a channel-owned session.
1596
+ * Omitted until HTTP accepts the first follow-up.
1597
+ */
1598
+ httpContinuationKey?: string;
1596
1599
  auth: AuthContext | null;
1597
1600
  title?: string;
1598
1601
  channelState?: JsonValue;
@@ -1668,7 +1671,7 @@ export interface SessionSummary {
1668
1671
  queued?: boolean;
1669
1672
  createdAt: string;
1670
1673
  updatedAt: string;
1671
- /** `<channelId>:<key>` wire token, or null when not addressable. */
1674
+ /** Active wire token for HTTP follow-ups, or null when not addressable. */
1672
1675
  continuationToken: string | null;
1673
1676
  /**
1674
1677
  * Conversation permalink (Slack thread, else GitHub PR).
@@ -0,0 +1,37 @@
1
+ # Security help
2
+
3
+ Answers security questions in Slack from your FAQ files and takes a first
4
+ look when someone pastes a GitHub pull request.
5
+
6
+ ```bash
7
+ npx @cursor/july init ./security-help --template security-help
8
+ ```
9
+
10
+ `init` asks which repos it may read. Skip the prompt with
11
+ `--var repos=owner/repo`.
12
+
13
+ ## Run
14
+
15
+ ```bash
16
+ agent-sdk login
17
+ agent-sdk slack create --name "Security Help" --channel-posts
18
+ agent-sdk dev
19
+ ```
20
+
21
+ Slack is a dedicated bot (`@Security Help`). Mentions, DMs, and channel
22
+ watch go through that app. GitHub reads use your Cursor GitHub connection
23
+ for the repos in `agent/lib/repos.ts`. Paste a PR URL in Slack, or replay
24
+ one in the playground.
25
+
26
+ ## Edit
27
+
28
+ - `agent/lib/repos.ts`: repos it may read
29
+ - `agent/channels/slack.ts`: channel ids to watch (`watch`)
30
+ - `agent/knowledge/faq/`: answers for your company
31
+ - `agent/skills/`: how to look at a PR, a leak, or an access ask
32
+ - `agent/instructions.md`: routing
33
+
34
+ `--channel-posts` is required to watch channels. Mentions and DMs work
35
+ without it.
36
+
37
+ `agent-sdk eval` runs three smoke turns and writes nothing to Slack or GitHub.
@@ -0,0 +1,18 @@
1
+ import { defineAgent } from "@cursor/july";
2
+
3
+ export default defineAgent({
4
+ name: "Security Help",
5
+ description:
6
+ "Answers security questions in Slack and takes a first look at a GitHub pull request.",
7
+ model: {
8
+ id: "grok-4.6",
9
+ params: [
10
+ { id: "effort", value: "high" },
11
+ { id: "fast", value: "true" },
12
+ ],
13
+ },
14
+ tools: ["read", "grep"],
15
+ local: {
16
+ sandbox: true,
17
+ },
18
+ });
@@ -0,0 +1,13 @@
1
+ import { githubChannel } from "@cursor/july/channels/github";
2
+ import { REPOS } from "../lib/repos.js";
3
+
4
+ export default githubChannel({
5
+ webhookEvents: [],
6
+ deliverReplies: false,
7
+ cursorAccount: {
8
+ repos: REPOS,
9
+ permissions: "read",
10
+ },
11
+ onPullRequest: () => null,
12
+ onComment: () => null,
13
+ });
@@ -0,0 +1,89 @@
1
+ import {
2
+ defaultSlackAuth,
3
+ extractGithubPrRef,
4
+ type SlackMessageHandler,
5
+ slackChannel,
6
+ } from "@cursor/july/channels/slack";
7
+ import { loadPrFiles } from "../lib/pr-first-pass.js";
8
+ import { REPOS, watches } from "../lib/repos.js";
9
+
10
+ const watch: string[] = [];
11
+
12
+ const clear = {
13
+ "pr/meta.md": "",
14
+ "pr/diff.patch": "",
15
+ "pr/head.sha": "",
16
+ };
17
+
18
+ const handleSlack: SlackMessageHandler = async (ctx, message) => {
19
+ const auth = defaultSlackAuth(message, ctx);
20
+ if (auth === null) {
21
+ return null;
22
+ }
23
+
24
+ const pr = extractGithubPrRef(message.markdown);
25
+ if (pr === undefined) {
26
+ return { auth };
27
+ }
28
+
29
+ const title = `Security Help ${pr.label}`;
30
+ const fullName = `${pr.owner}/${pr.repo}`;
31
+ if (!watches(fullName)) {
32
+ return {
33
+ auth,
34
+ title,
35
+ workspaceFiles: clear,
36
+ context: [
37
+ `Security help can only fetch ${REPOS.join(", ")} (got ${fullName}).`,
38
+ ],
39
+ };
40
+ }
41
+
42
+ const loaded = await loadPrFiles({
43
+ github: ctx.host.github,
44
+ ref: pr,
45
+ });
46
+ return {
47
+ auth,
48
+ title,
49
+ workspaceFiles: loaded ?? clear,
50
+ context:
51
+ loaded === undefined
52
+ ? ["Could not fetch the pull request. Connect GitHub or check the URL."]
53
+ : undefined,
54
+ };
55
+ };
56
+
57
+ export default slackChannel({
58
+ envPrefix: "SECURITY_HELP",
59
+ policy: {
60
+ blockExternals: true,
61
+ },
62
+ engagement: {
63
+ channelPosts:
64
+ watch.length === 0
65
+ ? undefined
66
+ : {
67
+ allow: watch,
68
+ posts: "top-level" as const,
69
+ },
70
+ },
71
+ onAppMention: handleSlack,
72
+ onDirectMessage: handleSlack,
73
+ onChannelPost: watch.length === 0 ? undefined : handleSlack,
74
+ suggestedPrompts: [
75
+ {
76
+ title: "Look at a PR",
77
+ message: "First look at https://github.com/owner/repo/pull/1",
78
+ },
79
+ {
80
+ title: "How urgent",
81
+ message: "How urgent is this?",
82
+ },
83
+ {
84
+ title: "Ask for context",
85
+ message: "What else do we need before answering?",
86
+ },
87
+ ],
88
+ suggestedPromptsTitle: "Ask security",
89
+ });
@@ -0,0 +1,20 @@
1
+ # Security help
2
+
3
+ Answer security questions in Slack from the FAQ and skills.
4
+ If you do not know, say so and ask a human.
5
+
6
+ Open the skill or FAQ for the ask. Those files are under `agent/skills/` and `agent/knowledge/faq/`.
7
+
8
+ | If the ask is about | Open |
9
+ | --- | --- |
10
+ | A GitHub pull request | `agent/skills/security-pr-first-pass.md` |
11
+ | An access request | `agent/skills/access-request.md` |
12
+ | A leak, alert, or incident | `agent/skills/security-playbooks.md` |
13
+ | Who can approve, privacy, or policy | `agent/knowledge/faq/approvals.md` |
14
+ | Phishing text, email, or a call | `agent/knowledge/faq/phishing.md` |
15
+ | Which team or channel owns a request | `agent/knowledge/faq/channels.md` |
16
+ | Another security question | `agent/skills/security-first-pass.md` |
17
+
18
+ A pull request earlier in the thread is not this one.
19
+
20
+ If you say an incident is over or a system is safe, show why. Looking at a pull request is not that.
@@ -0,0 +1,5 @@
1
+ # Approvals
2
+
3
+ Who can say yes depends on the request. If this file does not name an owner, ask a human for that topic.
4
+
5
+ Privacy questions (training, retention, where prompts sit) go to a privacy or security person.
@@ -0,0 +1,6 @@
1
+ # Channels
2
+
3
+ `#security` is for security questions and phishing reports.
4
+
5
+ Name the channel that owns SaaS access, identity groups, laptops, and billing.
6
+ Until you do, say you do not know which team owns that request.
@@ -0,0 +1,10 @@
1
+ # Phishing
2
+
3
+ `#security` is where phishing and unexpected texts, emails, or calls go.
4
+ If the thread is already about that, keep it there.
5
+
6
+ Leave the original message alone. Do not reply, click, or send a 2FA code on it.
7
+ Confirm unknown texts or calls over Slack or in person before acting.
8
+
9
+ If an account looks compromised, they should lock it now and wait for a human.
10
+ If security already gave a verdict in-thread, follow that.