@cursor/july 0.1.55 → 0.1.61

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 (261) hide show
  1. package/dist/bin/agent-serve.js +30 -23
  2. package/dist/channels/github/github-channel.d.ts +12 -0
  3. package/dist/channels/github/github-channel.d.ts.map +1 -1
  4. package/dist/channels/github/github-channel.js +67 -15
  5. package/dist/channels/github/types.d.ts +27 -1
  6. package/dist/channels/github/types.d.ts.map +1 -1
  7. package/dist/channels/origin/cursor-account.d.ts +22 -0
  8. package/dist/channels/origin/cursor-account.d.ts.map +1 -0
  9. package/dist/channels/origin/cursor-account.js +35 -0
  10. package/dist/channels/origin/events.d.ts +10 -0
  11. package/dist/channels/origin/events.d.ts.map +1 -0
  12. package/dist/channels/origin/events.js +27 -0
  13. package/dist/channels/origin/index.d.ts +29 -0
  14. package/dist/channels/origin/index.d.ts.map +1 -0
  15. package/dist/channels/origin/index.js +28 -0
  16. package/dist/channels/origin/origin-channel.d.ts +68 -0
  17. package/dist/channels/origin/origin-channel.d.ts.map +1 -0
  18. package/dist/channels/origin/origin-channel.js +567 -0
  19. package/dist/channels/origin/origin-webhook.d.ts +38 -0
  20. package/dist/channels/origin/origin-webhook.d.ts.map +1 -0
  21. package/dist/channels/origin/origin-webhook.js +151 -0
  22. package/dist/channels/origin/types.d.ts +152 -0
  23. package/dist/channels/origin/types.d.ts.map +1 -0
  24. package/dist/channels/origin/types.js +1 -0
  25. package/dist/channels/slack/doctor.d.ts +17 -1
  26. package/dist/channels/slack/doctor.d.ts.map +1 -1
  27. package/dist/channels/slack/doctor.js +186 -10
  28. package/dist/docs/404.html +2 -2
  29. package/dist/docs/ab.html +4 -4
  30. package/dist/docs/assets/{app.CVQxhV85.js → app.CW3KJh72.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.QAPzJjql.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.CSxTCOFu.js → theme.Bz9AodBA.js} +2 -2
  34. package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.js → example-agents_fsd.md.ZeGEpAw_.js} +2 -2
  35. package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.lean.js → example-agents_fsd.md.ZeGEpAw_.lean.js} +1 -1
  36. package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.js → example-agents_security-reviewer.md.DBL1TwtP.js} +2 -2
  37. package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.lean.js → example-agents_security-reviewer.md.DBL1TwtP.lean.js} +1 -1
  38. package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.js +12 -0
  39. package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.lean.js +1 -0
  40. package/dist/docs/assets/{guides_github.md.B8QKtIpw.js → guides_github.md.CLLQJKRB.js} +4 -4
  41. package/dist/docs/assets/{guides_github.md.B8QKtIpw.lean.js → guides_github.md.CLLQJKRB.lean.js} +1 -1
  42. package/dist/docs/assets/index.md.DhRHS_-L.js +5 -0
  43. package/dist/docs/assets/{index.md.COeoSkcF.lean.js → index.md.DhRHS_-L.lean.js} +1 -1
  44. package/dist/docs/assets/{reference_cli.md.D_CCfG8R.js → reference_cli.md.Cl14Jfo3.js} +8 -2
  45. package/dist/docs/assets/{reference_cli.md.D_CCfG8R.lean.js → reference_cli.md.Cl14Jfo3.lean.js} +1 -1
  46. package/dist/docs/assets/scaffolding-agents.md.CrWSA0Ko.js +1 -0
  47. package/dist/docs/assets/{scaffolding-agents.md.mkc3B_ZW.lean.js → scaffolding-agents.md.CrWSA0Ko.lean.js} +1 -1
  48. package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.js +14 -0
  49. package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.lean.js +1 -0
  50. package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.js +4 -0
  51. package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.lean.js +1 -0
  52. package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.js +8 -0
  53. package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.lean.js +1 -0
  54. package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.js +2 -0
  55. package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.lean.js +1 -0
  56. package/dist/docs/building-with-agents.html +4 -4
  57. package/dist/docs/concepts.html +5 -5
  58. package/dist/docs/deployment.html +4 -4
  59. package/dist/docs/evals.html +4 -4
  60. package/dist/docs/example-agents/approval-buddy.html +4 -4
  61. package/dist/docs/example-agents/benny.html +4 -4
  62. package/dist/docs/example-agents/bugbot.html +4 -4
  63. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  64. package/dist/docs/example-agents/codeowners-review.html +4 -4
  65. package/dist/docs/example-agents/concierge.html +4 -4
  66. package/dist/docs/example-agents/fsd.html +5 -5
  67. package/dist/docs/example-agents/index.html +4 -4
  68. package/dist/docs/example-agents/knowledge-base.html +4 -4
  69. package/dist/docs/example-agents/oncall.html +4 -4
  70. package/dist/docs/example-agents/security-reviewer.html +5 -5
  71. package/dist/docs/example-agents/slack-agent.html +4 -4
  72. package/dist/docs/example-agents/weather-agent.html +4 -4
  73. package/dist/docs/guides/agent-to-agent.html +4 -4
  74. package/dist/docs/guides/cloud-runtime.html +4 -4
  75. package/dist/docs/guides/convert-automation.html +36 -0
  76. package/dist/docs/guides/github.html +7 -7
  77. package/dist/docs/guides/human-in-the-loop.html +4 -4
  78. package/dist/docs/guides/mcp-oauth.html +4 -4
  79. package/dist/docs/guides/opentelemetry.html +4 -4
  80. package/dist/docs/guides/slack.html +4 -4
  81. package/dist/docs/guides/webhooks.html +4 -4
  82. package/dist/docs/hashmap.json +1 -1
  83. package/dist/docs/hillclimbing.html +4 -4
  84. package/dist/docs/index.html +6 -6
  85. package/dist/docs/quickstart.html +4 -4
  86. package/dist/docs/reference/agent-config.html +4 -4
  87. package/dist/docs/reference/artifacts.html +4 -4
  88. package/dist/docs/reference/channels.html +4 -4
  89. package/dist/docs/reference/cli.html +12 -6
  90. package/dist/docs/reference/connections.html +4 -4
  91. package/dist/docs/reference/hooks.html +4 -4
  92. package/dist/docs/reference/http-api.html +4 -4
  93. package/dist/docs/reference/instructions.html +4 -4
  94. package/dist/docs/reference/playground.html +4 -4
  95. package/dist/docs/reference/project-layout.html +4 -4
  96. package/dist/docs/reference/prompt.html +4 -4
  97. package/dist/docs/reference/schedules.html +4 -4
  98. package/dist/docs/reference/sessions.html +4 -4
  99. package/dist/docs/reference/skills.html +4 -4
  100. package/dist/docs/reference/subagents.html +4 -4
  101. package/dist/docs/reference/tools.html +4 -4
  102. package/dist/docs/scaffolding-agents.html +5 -5
  103. package/dist/docs/storage.html +4 -4
  104. package/dist/docs/templates/agentic-owners.html +38 -0
  105. package/dist/docs/templates/demo.html +28 -0
  106. package/dist/docs/templates/pr-autofixer.html +32 -0
  107. package/dist/docs/templates/security-reviewer.html +26 -0
  108. package/dist/docs/templates/triage.html +5 -5
  109. package/dist/docs/troubleshooting.html +4 -4
  110. package/dist/index.d.ts +3 -3
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +2 -2
  113. package/dist/internal/cli-ax.d.ts +2 -0
  114. package/dist/internal/cli-ax.d.ts.map +1 -1
  115. package/dist/internal/cli-ax.js +20 -0
  116. package/dist/internal/cli-slack.d.ts +2 -0
  117. package/dist/internal/cli-slack.d.ts.map +1 -1
  118. package/dist/internal/cli-slack.js +11 -2
  119. package/dist/internal/cursor-event-relay.d.ts +59 -11
  120. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  121. package/dist/internal/cursor-event-relay.js +133 -26
  122. package/dist/internal/discovery.d.ts.map +1 -1
  123. package/dist/internal/discovery.js +11 -2
  124. package/dist/internal/distribution.d.ts.map +1 -1
  125. package/dist/internal/distribution.js +1 -0
  126. package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
  127. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
  128. package/dist/internal/handleAgentServeTrigger.js +97 -19
  129. package/dist/internal/init-interview.d.ts +67 -0
  130. package/dist/internal/init-interview.d.ts.map +1 -0
  131. package/dist/internal/init-interview.js +300 -0
  132. package/dist/internal/init-project.d.ts +4 -0
  133. package/dist/internal/init-project.d.ts.map +1 -1
  134. package/dist/internal/init-project.js +40 -16
  135. package/dist/internal/mcp-endpoint.d.ts +7 -0
  136. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  137. package/dist/internal/mcp-endpoint.js +7 -0
  138. package/dist/internal/server.d.ts.map +1 -1
  139. package/dist/internal/server.js +159 -57
  140. package/dist/internal/session-engine.d.ts.map +1 -1
  141. package/dist/internal/session-engine.js +20 -21
  142. package/dist/internal/session-links.d.ts +14 -0
  143. package/dist/internal/session-links.d.ts.map +1 -0
  144. package/dist/internal/session-links.js +69 -0
  145. package/dist/internal/sessions-client.d.ts.map +1 -1
  146. package/dist/internal/sessions-client.js +1 -0
  147. package/dist/playground/assets/index-ClbuaLpm.css +1 -0
  148. package/dist/playground/assets/{index-0oqXUkrR.js → index-SqkR13k_.js} +39 -39
  149. package/dist/playground/index.html +2 -2
  150. package/dist/types.d.ts +19 -12
  151. package/dist/types.d.ts.map +1 -1
  152. package/docs/README.md +15 -0
  153. package/docs/example-agents/fsd.md +3 -0
  154. package/docs/example-agents/security-reviewer.md +3 -0
  155. package/docs/guides/convert-automation.md +176 -0
  156. package/docs/guides/github.md +6 -2
  157. package/docs/reference/cli.md +34 -10
  158. package/docs/scaffolding-agents.md +3 -0
  159. package/docs/templates/agentic-owners.md +97 -0
  160. package/docs/templates/demo.md +84 -0
  161. package/docs/templates/pr-autofixer.md +136 -0
  162. package/docs/templates/security-reviewer.md +89 -0
  163. package/package.json +9 -1
  164. package/skills/github/SKILL.md +4 -5
  165. package/src/bin/agent-serve.ts +13 -6
  166. package/src/channels/github/github-channel.ts +95 -18
  167. package/src/channels/github/types.ts +31 -0
  168. package/src/channels/origin/cursor-account.ts +64 -0
  169. package/src/channels/origin/events.ts +41 -0
  170. package/src/channels/origin/index.ts +29 -0
  171. package/src/channels/origin/origin-channel.ts +784 -0
  172. package/src/channels/origin/origin-webhook.ts +202 -0
  173. package/src/channels/origin/types.ts +186 -0
  174. package/src/channels/slack/doctor.ts +218 -5
  175. package/src/index.ts +3 -1
  176. package/src/internal/cli-ax.ts +23 -0
  177. package/src/internal/cli-slack.ts +11 -1
  178. package/src/internal/cursor-event-relay.ts +193 -31
  179. package/src/internal/discovery.ts +15 -5
  180. package/src/internal/distribution.ts +1 -0
  181. package/src/internal/handleAgentServeTrigger.ts +129 -19
  182. package/src/internal/init-interview.ts +384 -0
  183. package/src/internal/init-project.ts +70 -15
  184. package/src/internal/mcp-endpoint.ts +7 -0
  185. package/src/internal/server.ts +188 -58
  186. package/src/internal/session-engine.ts +5 -21
  187. package/src/internal/session-links.ts +88 -0
  188. package/src/internal/sessions-client.ts +1 -0
  189. package/src/types.ts +19 -12
  190. package/templates/agentic-owners/README.md +73 -0
  191. package/templates/agentic-owners/agent/agent.ts +15 -0
  192. package/templates/agentic-owners/agent/channels/github.ts +85 -0
  193. package/templates/agentic-owners/agent/instructions.md +34 -0
  194. package/templates/agentic-owners/agent/lib/config.ts +13 -0
  195. package/templates/agentic-owners/agent/lib/github.ts +241 -0
  196. package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
  197. package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
  198. package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
  199. package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
  200. package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
  201. package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
  202. package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
  203. package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
  204. package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
  205. package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
  206. package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
  207. package/templates/agentic-owners/agent/lib/review.ts +777 -0
  208. package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
  209. package/templates/agentic-owners/evals/evals.config.ts +5 -0
  210. package/templates/agentic-owners/evals/owners.eval.ts +116 -0
  211. package/templates/agentic-owners/package.json +20 -0
  212. package/templates/agentic-owners/tsconfig.json +12 -0
  213. package/templates/demo/README.md +33 -0
  214. package/templates/demo/agent/agent.ts +15 -0
  215. package/templates/demo/agent/channels/github.ts +50 -0
  216. package/templates/demo/agent/channels/queue.ts +30 -0
  217. package/templates/demo/agent/instructions.md +8 -0
  218. package/templates/demo/agent/lib/collect.ts +78 -0
  219. package/templates/demo/agent/tools/record_demo.ts +40 -0
  220. package/templates/demo/evals/demo.eval.ts +33 -0
  221. package/templates/demo/evals/evals.config.ts +5 -0
  222. package/templates/demo/package.json +18 -0
  223. package/templates/demo/tsconfig.json +12 -0
  224. package/templates/pr-autofixer/README.md +44 -0
  225. package/templates/pr-autofixer/agent/agent.ts +22 -0
  226. package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
  227. package/templates/pr-autofixer/agent/channels/github.ts +167 -0
  228. package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
  229. package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
  230. package/templates/pr-autofixer/agent/instructions.md +83 -0
  231. package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
  232. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
  233. package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
  234. package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
  235. package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
  236. package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
  237. package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
  238. package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
  239. package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
  240. package/templates/pr-autofixer/agent/storage.ts +7 -0
  241. package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
  242. package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
  243. package/templates/pr-autofixer/evals/evals.config.ts +5 -0
  244. package/templates/pr-autofixer/init.json +25 -0
  245. package/templates/pr-autofixer/package.json +18 -0
  246. package/templates/pr-autofixer/tsconfig.json +12 -0
  247. package/templates/security-reviewer/README.md +31 -0
  248. package/templates/security-reviewer/agent/agent.ts +15 -0
  249. package/templates/security-reviewer/agent/channels/github.ts +46 -0
  250. package/templates/security-reviewer/agent/instructions.md +22 -0
  251. package/templates/security-reviewer/agent/lib/repos.ts +5 -0
  252. package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
  253. package/templates/security-reviewer/evals/evals.config.ts +5 -0
  254. package/templates/security-reviewer/evals/review.eval.ts +53 -0
  255. package/templates/security-reviewer/init.json +25 -0
  256. package/templates/security-reviewer/package.json +18 -0
  257. package/templates/security-reviewer/tsconfig.json +12 -0
  258. package/dist/docs/assets/chunks/@localSearchIndexroot.BFpWSo6D.js +0 -1
  259. package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
  260. package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
  261. package/dist/playground/assets/index-tHCjJxv4.css +0 -1
@@ -40,6 +40,7 @@ import {
40
40
  snapshotToEvalRunResults,
41
41
  startRemoteEvalRun,
42
42
  } from "./evals-client.js";
43
+ import { runTemplateInit, type TemplateInitSpec } from "./init-interview.js";
43
44
  import {
44
45
  checkInitTarget,
45
46
  formatInitNextSteps,
@@ -134,6 +135,8 @@ export interface AxCliOptions {
134
135
  /** Scaffold target directory for `init`. */
135
136
  initTarget?: string;
136
137
  template?: string;
138
+ /** `init --var id=value` (repeatable). */
139
+ vars?: string[];
137
140
  /** Tool name for `call`. */
138
141
  toolName?: string;
139
142
  /** JSON input for `call`. */
@@ -311,11 +314,31 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
311
314
  process.stderr.write(`${targetCheck.message}\n`);
312
315
  return 1;
313
316
  }
317
+ let answers: Record<string, string> | undefined;
318
+ let initSpec: TemplateInitSpec | undefined;
319
+ if (templateDir !== undefined) {
320
+ const interview = await runTemplateInit({
321
+ templateDir,
322
+ vars: options.vars,
323
+ interactive:
324
+ options.json !== true &&
325
+ process.stdin.isTTY === true &&
326
+ process.stdout.isTTY === true,
327
+ });
328
+ if (!interview.ok) {
329
+ process.stderr.write(`${interview.error}\n`);
330
+ return 1;
331
+ }
332
+ answers = interview.answers;
333
+ initSpec = interview.spec;
334
+ }
314
335
  const result = await initProject({
315
336
  dir,
316
337
  templateDir,
317
338
  overlayDir,
318
339
  templateName,
340
+ answers,
341
+ initSpec,
319
342
  });
320
343
  let signedIn = (await resolveApiKey()) !== undefined;
321
344
 
@@ -64,6 +64,8 @@ export interface SlackCliOptions {
64
64
  manual?: boolean;
65
65
  /** `slack icon <source>` positional (same URL-or-file forms as --icon). */
66
66
  iconSource?: string;
67
+ /** doctor: watched channel ids (`C…`) to verify visibility/membership. */
68
+ channels?: string[];
67
69
  /** Test seams. */
68
70
  fetchImpl?: typeof fetch;
69
71
  openBrowserImpl?: (url: string) => void;
@@ -91,6 +93,7 @@ Usage:
91
93
  ${CLI} slack manifest --env dev|prod|both [--dir .] [--name NAME]
92
94
  [--print]
93
95
  ${CLI} slack doctor [--dir .] [--prefix PREFIX | --no-prefix] [--json]
96
+ [--channel C0123ABCD]...
94
97
 
95
98
  create/destroy/icon — dedicated Socket Mode app via the Cursor dashboard:
96
99
  create Opens the signed-in dashboard wizard. Finish name, workspace,
@@ -155,7 +158,11 @@ async function runDoctor(options: SlackCliOptions): Promise<number> {
155
158
  loadAgentLocalEnv(dir);
156
159
  const envPrefix = resolveDoctorEnvPrefix(options, dir);
157
160
  const doctor = options.doctorImpl ?? runSlackDoctor;
158
- const result = await doctor(envPrefix === undefined ? {} : { envPrefix });
161
+ const channels = options.channels ?? [];
162
+ const result = await doctor({
163
+ envPrefix,
164
+ channels: channels.length > 0 ? channels : undefined,
165
+ });
159
166
  if (options.json) {
160
167
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
161
168
  } else {
@@ -879,6 +886,9 @@ function renderDoctorReport(result: SlackDoctorResult): string {
879
886
  text += `${check.ok ? "ok" : "!!"} ${check.id}: ${check.message}\n`;
880
887
  }
881
888
  text += result.ok ? "doctor: pass\n" : "doctor: fail\n";
889
+ for (const note of result.notes ?? []) {
890
+ text += `\nnote: ${note}\n`;
891
+ }
882
892
  return text;
883
893
  }
884
894
 
@@ -115,6 +115,12 @@ export interface CursorScmEvent {
115
115
  * Zod-valid `issue_comment` / `pull_request_review_comment` webhook body.
116
116
  */
117
117
  comment?: CursorScmCommentMeta;
118
+ /**
119
+ * Origin comment snapshot. Ids are Origin string ids, not GitHub integers.
120
+ */
121
+ originComment?: CursorScmOriginCommentMeta;
122
+ /** Origin actor (author / pusher / commenter) when present. */
123
+ originActor?: CursorScmOriginActorMeta;
118
124
  /** `check_run` snapshot; enough to stub `onCheckRun`. */
119
125
  checkRun?: CursorScmCheckRunMeta;
120
126
  /** `check_suite` snapshot; enough to stub `onCheckSuite`. */
@@ -125,6 +131,25 @@ export interface CursorScmEvent {
125
131
  occurredAtMs?: number;
126
132
  }
127
133
 
134
+ export type CursorScmOriginActorMeta = {
135
+ id: string;
136
+ kind?: string;
137
+ };
138
+
139
+ export type CursorScmOriginCommentMeta = {
140
+ id: string;
141
+ body?: string;
142
+ author?: CursorScmOriginActorMeta;
143
+ };
144
+
145
+ export type CursorScmProvider = "github" | "origin";
146
+
147
+ export interface CursorScmSubscription {
148
+ provider: CursorScmProvider;
149
+ repo: string;
150
+ events: readonly string[];
151
+ }
152
+
128
153
  interface SubscriptionStatus {
129
154
  provider: string;
130
155
  repo: string;
@@ -135,8 +160,17 @@ interface SubscriptionStatus {
135
160
  export interface CursorEventRelayOptions {
136
161
  baseUrl: string;
137
162
  apiKey: string;
138
- repos: readonly string[];
139
- events: readonly string[];
163
+ /**
164
+ * GitHub-only shorthand. Ignored when {@link subscriptions} is set.
165
+ * Existing `--cursor-events --repo` hosts keep passing these.
166
+ */
167
+ repos?: readonly string[];
168
+ events?: readonly string[];
169
+ /**
170
+ * Mixed GitHub + Origin registrations. Provider is part of the
171
+ * subscription key: mirrored `owner/name` slugs must not collide.
172
+ */
173
+ subscriptions?: readonly CursorScmSubscription[];
140
174
  stateDir: string;
141
175
  /**
142
176
  * Called for each matching event; failures retry a bounded number of
@@ -430,30 +464,149 @@ function stubGitHubWorkflowRun(
430
464
  }
431
465
 
432
466
  /**
433
- * GitHub SCM-events relay: a thin {@link CursorRelayCore} adapter. Consumers
467
+ * Origin webhook envelope from an SCM-events metadata record. One parser
468
+ * handles both live Origin deliveries and this stub: `{ deliveryId, event }`.
469
+ */
470
+ export function scmMetadataToOriginPayload(event: {
471
+ repo: string;
472
+ eventName: string;
473
+ deliveryId?: string;
474
+ action?: string;
475
+ prNumber?: number;
476
+ prUrl?: string;
477
+ pullRequest?: CursorScmPullRequestMeta;
478
+ originComment?: CursorScmOriginCommentMeta;
479
+ originActor?: CursorScmOriginActorMeta;
480
+ occurredAtMs?: number;
481
+ }): Record<string, unknown> {
482
+ const slash = event.repo.indexOf("/");
483
+ const owner = slash === -1 ? "" : event.repo.slice(0, slash);
484
+ const name = slash === -1 ? event.repo : event.repo.slice(slash + 1);
485
+ const prMeta = event.pullRequest;
486
+ const prNumber = prMeta?.number ?? event.prNumber;
487
+ const prUrl =
488
+ prMeta?.url ??
489
+ event.prUrl ??
490
+ (prNumber !== undefined
491
+ ? `https://cursor.com/codebase/${owner}/${name}/pull/${prNumber}`
492
+ : undefined);
493
+
494
+ const payload: Record<string, unknown> = {
495
+ repository: {
496
+ name,
497
+ owner: { slug: owner },
498
+ },
499
+ };
500
+
501
+ if (prNumber !== undefined) {
502
+ const pullRequest: Record<string, unknown> = {
503
+ number: prNumber,
504
+ url: prUrl,
505
+ };
506
+ if (prMeta?.title !== undefined) pullRequest.title = prMeta.title;
507
+ if (prMeta?.body !== undefined) pullRequest.body = prMeta.body;
508
+ if (prMeta?.draft !== undefined) pullRequest.draft = prMeta.draft;
509
+ if (prMeta?.merged !== undefined) pullRequest.merged = prMeta.merged;
510
+ if (prMeta?.headRef !== undefined || prMeta?.headSha !== undefined) {
511
+ pullRequest.head = {
512
+ ref: prMeta.headRef,
513
+ sha: prMeta.headSha,
514
+ };
515
+ }
516
+ if (prMeta?.baseRef !== undefined || prMeta?.baseSha !== undefined) {
517
+ pullRequest.base = {
518
+ ref: prMeta.baseRef,
519
+ sha: prMeta.baseSha,
520
+ };
521
+ }
522
+ if (prMeta?.authorLogin !== undefined) {
523
+ pullRequest.author = { id: prMeta.authorLogin };
524
+ }
525
+ payload.pullRequest = pullRequest;
526
+ }
527
+
528
+ const commentMeta = event.originComment;
529
+ if (commentMeta !== undefined) {
530
+ const comment: Record<string, unknown> = { id: commentMeta.id };
531
+ if (commentMeta.body !== undefined) comment.body = commentMeta.body;
532
+ if (commentMeta.author !== undefined) comment.author = commentMeta.author;
533
+ payload.comment = comment;
534
+ }
535
+ if (event.originActor !== undefined) {
536
+ payload.actor = event.originActor;
537
+ }
538
+
539
+ return {
540
+ deliveryId: event.deliveryId,
541
+ event: {
542
+ type: event.eventName,
543
+ ...(event.occurredAtMs === undefined
544
+ ? {}
545
+ : { eventTime: new Date(event.occurredAtMs).toISOString() }),
546
+ payload,
547
+ },
548
+ };
549
+ }
550
+
551
+ function subscriptionKey(provider: string, repo: string): string {
552
+ return `${provider}:${repo.toLowerCase()}`;
553
+ }
554
+
555
+ function subscriptionLabel(provider: string, repo: string): string {
556
+ return provider === "github" ? repo : `${provider}:${repo}`;
557
+ }
558
+
559
+ export function resolveCursorScmSubscriptions(
560
+ options: Pick<CursorEventRelayOptions, "repos" | "events" | "subscriptions">
561
+ ): CursorScmSubscription[] {
562
+ if (options.subscriptions !== undefined) {
563
+ return options.subscriptions.map((subscription) => ({
564
+ provider: subscription.provider,
565
+ repo: subscription.repo.trim().toLowerCase(),
566
+ events: [...subscription.events],
567
+ }));
568
+ }
569
+ const events = [...(options.events ?? [])];
570
+ return (options.repos ?? []).map((repo) => ({
571
+ provider: "github" as const,
572
+ repo: repo.trim().toLowerCase(),
573
+ events,
574
+ }));
575
+ }
576
+
577
+ /**
578
+ * SCM-events relay: a thin {@link CursorRelayCore} adapter. Consumers
434
579
  * keep the same `new CursorEventRelay(options).start()/.stop()` surface.
435
580
  */
436
581
  export class CursorEventRelay {
437
582
  private readonly core: CursorRelayCore<CursorScmEvent>;
583
+ private readonly subscriptions: CursorScmSubscription[];
438
584
  /**
439
- * Normalized repos the server confirmed for THIS consumer. The stream is
440
- * shared with sibling consumers of the same principal, so only events for
441
- * these repos may dispatch.
585
+ * Confirmed `provider:repo` keys for THIS consumer, with the event kinds
586
+ * requested for each. The stream is shared with sibling consumers, so
587
+ * GitHub and Origin deliveries for the same owner/name must not mix.
442
588
  */
443
- private subscribedRepos = new Set<string>();
444
- private readonly loggedSkippedRepos = new Set<string>();
589
+ private subscribedEvents = new Map<string, ReadonlySet<string>>();
590
+ private readonly loggedSkippedKeys = new Set<string>();
445
591
 
446
592
  constructor(private readonly options: CursorEventRelayOptions) {
593
+ this.subscriptions = resolveCursorScmSubscriptions(options);
594
+ const requestedEvents = new Map(
595
+ this.subscriptions.map((subscription) => [
596
+ subscriptionKey(subscription.provider, subscription.repo),
597
+ new Set(subscription.events),
598
+ ])
599
+ );
447
600
  const adapter: RelayAdapter<CursorScmEvent> = {
448
601
  logPrefix: "cursor-events",
449
602
  subscribePath: "/v0/scm-events/subscriptions",
450
603
  pollPath: "/v0/scm-events",
451
604
  buildRegistrationBody: (consumerId) => ({
452
605
  consumerId,
453
- subscriptions: this.options.repos.map((repo) => ({
454
- provider: "github",
455
- repo,
456
- events: [...this.options.events],
606
+ subscriptions: this.subscriptions.map((subscription) => ({
607
+ provider: subscription.provider,
608
+ repo: subscription.repo,
609
+ events: [...subscription.events],
457
610
  })),
458
611
  }),
459
612
  // Empty subscriptions drop this consumer's registration on clean stop
@@ -465,20 +618,27 @@ export class CursorEventRelay {
465
618
  applyRegistrationResponse: (json) => {
466
619
  const result = json as { repos?: SubscriptionStatus[] };
467
620
  const statuses = result.repos ?? [];
468
- this.subscribedRepos = new Set(
469
- statuses
470
- .filter((status) => status.isSubscribed)
471
- .map((status) => status.repo)
472
- );
621
+ this.subscribedEvents = new Map();
473
622
  const lines: string[] = [];
474
- for (const status of statuses.filter((s) => !s.isSubscribed)) {
475
- lines.push(
476
- `not subscribed to ${status.repo}: ${status.detail ?? "unknown reason"}`
477
- );
623
+ const confirmed: string[] = [];
624
+ for (const status of statuses) {
625
+ const provider = status.provider || "github";
626
+ const key = subscriptionKey(provider, status.repo);
627
+ if (!status.isSubscribed) {
628
+ lines.push(
629
+ `not subscribed to ${subscriptionLabel(provider, status.repo)}: ${status.detail ?? "unknown reason"}`
630
+ );
631
+ continue;
632
+ }
633
+ this.subscribedEvents.set(key, requestedEvents.get(key) ?? new Set());
634
+ confirmed.push(subscriptionLabel(provider, status.repo));
478
635
  }
479
- if (this.subscribedRepos.size > 0) {
636
+ if (confirmed.length > 0) {
637
+ const eventKinds = [
638
+ ...new Set(this.subscriptions.flatMap((s) => [...s.events])),
639
+ ];
480
640
  lines.push(
481
- `subscribed to ${[...this.subscribedRepos].join(", ")} for events: ${this.options.events.join(", ")}`
641
+ `subscribed to ${confirmed.join(", ")} for events: ${eventKinds.join(", ")}`
482
642
  );
483
643
  }
484
644
  return lines;
@@ -503,21 +663,23 @@ export class CursorEventRelay {
503
663
 
504
664
  /**
505
665
  * Whether an event from the shared stream belongs to THIS consumer: the
506
- * repo must be one the server confirmed for us and the event kind one the
507
- * mounted channels declared. Anything else came from a sibling consumer's
508
- * subscription and must not fire this host's hooks.
666
+ * provider+repo must be one the server confirmed for us and the event kind
667
+ * one that subscription declared. GitHub and Origin share owner/name slugs
668
+ * on mirrors, so the provider is part of the key.
509
669
  */
510
670
  private shouldDispatch(event: CursorScmEvent): boolean {
511
- if (!this.subscribedRepos.has(event.repo)) {
512
- if (!this.loggedSkippedRepos.has(event.repo)) {
513
- this.loggedSkippedRepos.add(event.repo);
671
+ const key = subscriptionKey(event.provider || "github", event.repo);
672
+ const events = this.subscribedEvents.get(key);
673
+ if (events === undefined) {
674
+ if (!this.loggedSkippedKeys.has(key)) {
675
+ this.loggedSkippedKeys.add(key);
514
676
  this.options.logger(
515
- `[agent-sdk] cursor-events: skipping events for ${event.repo}: not in this host's subscription (another consumer of this API key likely subscribed it)`
677
+ `[agent-sdk] cursor-events: skipping events for ${subscriptionLabel(event.provider || "github", event.repo)}: not in this host's subscription (another consumer of this API key likely subscribed it)`
516
678
  );
517
679
  }
518
680
  return false;
519
681
  }
520
- return this.options.events.includes(event.eventName);
682
+ return events.has(event.eventName);
521
683
  }
522
684
 
523
685
  start(): void {
@@ -16,6 +16,7 @@ import {
16
16
  relative,
17
17
  resolve,
18
18
  } from "node:path";
19
+ import { pathToFileURL } from "node:url";
19
20
  import { type ABConfigFile, resolveABMaxPlaygroundSessions } from "../ab.js";
20
21
  import { resolveArtifactsMax } from "../artifacts.js";
21
22
  import { httpChannel } from "../channels.js";
@@ -60,10 +61,7 @@ import {
60
61
  type ToolDefinition,
61
62
  toPublicCloudInfo,
62
63
  } from "../types.js";
63
- import {
64
- importAuthoredModule,
65
- registerAuthoredModuleLoaders,
66
- } from "./authored-loaders.js";
64
+ import { registerAuthoredModuleLoaders } from "./authored-loaders.js";
67
65
  import { normalizePositiveInt } from "./bounded-int.js";
68
66
  import { getDefinitionKind } from "./brand.js";
69
67
  import {
@@ -96,6 +94,15 @@ export interface LoadAgentProjectOptions {
96
94
 
97
95
  type ImportModule = (absolutePath: string) => Promise<Record<string, unknown>>;
98
96
 
97
+ async function defaultImportModule(
98
+ absolutePath: string
99
+ ): Promise<Record<string, unknown>> {
100
+ return (await import(pathToFileURL(absolutePath).href)) as Record<
101
+ string,
102
+ unknown
103
+ >;
104
+ }
105
+
99
106
  interface DiscoveryContext {
100
107
  rootDir: string;
101
108
  diagnostics: Diagnostic[];
@@ -275,7 +282,7 @@ export async function loadAgentProject(
275
282
  const ctx: DiscoveryContext = {
276
283
  rootDir,
277
284
  diagnostics,
278
- importModule: options.importModule ?? importAuthoredModule,
285
+ importModule: options.importModule ?? defaultImportModule,
279
286
  };
280
287
 
281
288
  const name = (await readPackageName(rootDir)) ?? basename(rootDir);
@@ -1998,6 +2005,9 @@ export function projectInfo(project: AgentProject): AgentProjectInfo {
1998
2005
  if (meta?.githubWebhookEvents !== undefined) {
1999
2006
  info.githubWebhookEvents = meta.githubWebhookEvents;
2000
2007
  }
2008
+ if (meta?.originWebhookEvents !== undefined) {
2009
+ info.originWebhookEvents = meta.originWebhookEvents;
2010
+ }
2001
2011
  if (meta?.slackChannelPosts !== undefined) {
2002
2012
  info.slackChannelPosts = meta.slackChannelPosts;
2003
2013
  }
@@ -96,6 +96,7 @@ export const AUTHORING_ENTRY_FILES: ReadonlyArray<
96
96
  ["channels", "channels.js"],
97
97
  ["channels/slack", "channels/slack/index.js"],
98
98
  ["channels/github", "channels/github/index.js"],
99
+ ["channels/origin", "channels/origin/index.js"],
99
100
  ["skills", "skills.js"],
100
101
  ["schedules", "schedules.js"],
101
102
  ["reminders", "reminders.js"],
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Unified entry for dispatching agent-serve triggers from an external ingress
3
- * (e.g. the Cursor backend GitHub webhook). Mirrors Automations'
4
- * `handleAutomationTrigger`, but only GitHub is supported for now.
3
+ * (e.g. the Cursor backend GitHub webhook or Origin cursor-internal tap).
4
+ * Mirrors Automations' `handleAutomationTrigger`.
5
5
  *
6
6
  * Callers must authenticate the delivery before invoking this (the backend
7
- * verifies GitHub HMAC at `/github_webhook`). This fan-out does not re-check
8
- * signatures same trust model as Bugbot after `verifyGithubRequest`.
7
+ * verifies GitHub HMAC at `/github_webhook` and Origin v1ed at the
8
+ * cursor-internal app). This fan-out does not re-check signatures — same
9
+ * trust model as Bugbot after `verifyGithubRequest`.
9
10
  *
10
11
  * Each agent's `onPullRequest` / `onComment` / … hooks decide whether to run.
11
12
  */
@@ -15,6 +16,12 @@ import {
15
16
  getGitHubChannelOptions,
16
17
  } from "../channels/github/github-channel.js";
17
18
  import type { GitHubChannelOptions } from "../channels/github/types.js";
19
+ import {
20
+ dispatchOriginWebhook,
21
+ getOriginChannelOptions,
22
+ parseOriginWebhookEnvelope,
23
+ } from "../channels/origin/origin-channel.js";
24
+ import type { OriginChannelOptions } from "../channels/origin/types.js";
18
25
  import type { ChannelHandlerArgs } from "../types.js";
19
26
 
20
27
  export interface AgentServeGitHubTriggerTarget {
@@ -35,7 +42,22 @@ export interface AgentServeGitHubTriggerEvent {
35
42
  targets: AgentServeGitHubTriggerTarget[];
36
43
  }
37
44
 
38
- export type AgentServeTriggerEvent = AgentServeGitHubTriggerEvent;
45
+ export interface AgentServeOriginTriggerTarget {
46
+ slug: string;
47
+ options: OriginChannelOptions;
48
+ args: ChannelHandlerArgs;
49
+ }
50
+
51
+ export interface AgentServeOriginTriggerEvent {
52
+ type: "origin";
53
+ /** Origin envelope JSON (signature already verified by the caller). */
54
+ bodyText: string;
55
+ targets: AgentServeOriginTriggerTarget[];
56
+ }
57
+
58
+ export type AgentServeTriggerEvent =
59
+ | AgentServeGitHubTriggerEvent
60
+ | AgentServeOriginTriggerEvent;
39
61
 
40
62
  export interface AgentServeTriggerTargetResult {
41
63
  slug: string;
@@ -78,17 +100,46 @@ export function collectGitHubTriggerTargets(input: {
78
100
  }
79
101
 
80
102
  /**
81
- * Dispatch an agent-serve trigger. GitHub only for now.
103
+ * Build fan-out targets from mounted agent projects that export an origin
104
+ * channel created via {@link originChannel}.
105
+ */
106
+ export function collectOriginTriggerTargets(input: {
107
+ mounts: Array<{
108
+ slug: string;
109
+ channels: Array<{ id: string; definition: object }>;
110
+ buildHandlerArgs: (channelId: string) => ChannelHandlerArgs;
111
+ }>;
112
+ }): AgentServeOriginTriggerTarget[] {
113
+ const targets: AgentServeOriginTriggerTarget[] = [];
114
+ for (const mount of input.mounts) {
115
+ for (const channel of mount.channels) {
116
+ const options = getOriginChannelOptions(channel.definition);
117
+ if (options === undefined) {
118
+ continue;
119
+ }
120
+ targets.push({
121
+ slug: mount.slug,
122
+ options,
123
+ args: mount.buildHandlerArgs(channel.id),
124
+ });
125
+ }
126
+ }
127
+ return targets;
128
+ }
129
+
130
+ /**
131
+ * Dispatch an agent-serve trigger. GitHub and Origin webhook fan-out.
82
132
  */
83
133
  export async function handleAgentServeTrigger(
84
134
  event: AgentServeTriggerEvent
85
135
  ): Promise<AgentServeTriggerResult> {
86
136
  switch (event.type) {
87
- // TODO: Add support for other event types (e.g. Linear, Slack, etc.).
88
137
  case "github":
89
138
  return handleAgentServeGitHubTrigger(event);
139
+ case "origin":
140
+ return handleAgentServeOriginTrigger(event);
90
141
  default: {
91
- const _: never = event.type;
142
+ const _: never = event;
92
143
  throw new Error(
93
144
  `handleAgentServeTrigger: unknown event type: ${String(_)}`
94
145
  );
@@ -133,17 +184,7 @@ async function handleAgentServeGitHubTrigger(
133
184
  deliveryId: event.deliveryId,
134
185
  args: target.args,
135
186
  });
136
- let body: unknown;
137
- try {
138
- body = await response.json();
139
- } catch {
140
- body = { ok: false, error: "invalid_response_json" };
141
- }
142
- targets.push({
143
- slug: target.slug,
144
- status: response.status,
145
- body,
146
- });
187
+ targets.push(await triggerTargetResult(target.slug, response));
147
188
  }
148
189
 
149
190
  return {
@@ -152,3 +193,72 @@ async function handleAgentServeGitHubTrigger(
152
193
  targets,
153
194
  };
154
195
  }
196
+
197
+ async function handleAgentServeOriginTrigger(
198
+ event: AgentServeOriginTriggerEvent
199
+ ): Promise<AgentServeTriggerResult> {
200
+ let payload: unknown;
201
+ try {
202
+ payload = JSON.parse(event.bodyText) as unknown;
203
+ } catch {
204
+ return {
205
+ ok: false,
206
+ event: null,
207
+ targets: [
208
+ {
209
+ slug: "",
210
+ status: 400,
211
+ body: { ok: false, error: "invalid_json" },
212
+ },
213
+ ],
214
+ };
215
+ }
216
+
217
+ const envelope = parseOriginWebhookEnvelope(payload);
218
+ if (envelope === null) {
219
+ return {
220
+ ok: false,
221
+ event: null,
222
+ targets: [
223
+ {
224
+ slug: "",
225
+ status: 400,
226
+ body: { ok: false, error: "invalid_origin_payload" },
227
+ },
228
+ ],
229
+ };
230
+ }
231
+
232
+ const targets: AgentServeTriggerTargetResult[] = [];
233
+ for (const target of event.targets) {
234
+ const response = await dispatchOriginWebhook({
235
+ options: target.options,
236
+ envelope,
237
+ args: target.args,
238
+ });
239
+ targets.push(await triggerTargetResult(target.slug, response));
240
+ }
241
+
242
+ return {
243
+ ok: true,
244
+ event: envelope.event.type,
245
+ targets,
246
+ };
247
+ }
248
+
249
+ async function triggerTargetResult(
250
+ slug: string,
251
+ response: Response
252
+ ): Promise<AgentServeTriggerTargetResult> {
253
+ let body: unknown;
254
+ try {
255
+ body = await response.json();
256
+ } catch {
257
+ body = { ok: false, error: "invalid_response_json" };
258
+ }
259
+ return {
260
+ slug,
261
+ status: response.status,
262
+ body,
263
+ };
264
+ }