@cursor/july 0.1.55 → 0.1.62

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 (265) hide show
  1. package/dist/bin/agent-serve.js +30 -23
  2. package/dist/channels/github/github-channel.d.ts +13 -1
  3. package/dist/channels/github/github-channel.d.ts.map +1 -1
  4. package/dist/channels/github/github-channel.js +81 -16
  5. package/dist/channels/github/types.d.ts +29 -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.B-biogkN.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.5ozYNf74.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.QAPzJjql.js → VPLocalSearchBox.DZqKtFZS.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.CSxTCOFu.js → theme.BwC5zP2r.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 +64 -12
  120. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  121. package/dist/internal/cursor-event-relay.js +138 -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/mcp-oauth.d.ts +0 -4
  139. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  140. package/dist/internal/mcp-oauth.js +0 -4
  141. package/dist/internal/server.d.ts.map +1 -1
  142. package/dist/internal/server.js +160 -58
  143. package/dist/internal/session-engine.d.ts.map +1 -1
  144. package/dist/internal/session-engine.js +20 -21
  145. package/dist/internal/session-links.d.ts +14 -0
  146. package/dist/internal/session-links.d.ts.map +1 -0
  147. package/dist/internal/session-links.js +69 -0
  148. package/dist/internal/sessions-client.d.ts.map +1 -1
  149. package/dist/internal/sessions-client.js +1 -0
  150. package/dist/playground/assets/index-B4rcOgeP.css +1 -0
  151. package/dist/playground/assets/{index-0oqXUkrR.js → index-DXcY42Jv.js} +42 -42
  152. package/dist/playground/index.html +2 -2
  153. package/dist/types.d.ts +19 -12
  154. package/dist/types.d.ts.map +1 -1
  155. package/docs/README.md +15 -0
  156. package/docs/example-agents/fsd.md +3 -0
  157. package/docs/example-agents/security-reviewer.md +3 -0
  158. package/docs/guides/convert-automation.md +176 -0
  159. package/docs/guides/github.md +6 -2
  160. package/docs/reference/cli.md +34 -10
  161. package/docs/scaffolding-agents.md +3 -0
  162. package/docs/templates/agentic-owners.md +97 -0
  163. package/docs/templates/demo.md +84 -0
  164. package/docs/templates/pr-autofixer.md +136 -0
  165. package/docs/templates/security-reviewer.md +89 -0
  166. package/package.json +9 -1
  167. package/skills/github/SKILL.md +6 -6
  168. package/src/bin/agent-serve.ts +13 -6
  169. package/src/channels/github/github-channel.ts +113 -20
  170. package/src/channels/github/types.ts +33 -0
  171. package/src/channels/origin/cursor-account.ts +64 -0
  172. package/src/channels/origin/events.ts +41 -0
  173. package/src/channels/origin/index.ts +29 -0
  174. package/src/channels/origin/origin-channel.ts +784 -0
  175. package/src/channels/origin/origin-webhook.ts +202 -0
  176. package/src/channels/origin/types.ts +186 -0
  177. package/src/channels/slack/doctor.ts +218 -5
  178. package/src/index.ts +3 -1
  179. package/src/internal/cli-ax.ts +23 -0
  180. package/src/internal/cli-slack.ts +11 -1
  181. package/src/internal/cursor-event-relay.ts +201 -32
  182. package/src/internal/discovery.ts +15 -5
  183. package/src/internal/distribution.ts +1 -0
  184. package/src/internal/handleAgentServeTrigger.ts +129 -19
  185. package/src/internal/init-interview.ts +384 -0
  186. package/src/internal/init-project.ts +70 -15
  187. package/src/internal/mcp-endpoint.ts +7 -0
  188. package/src/internal/mcp-oauth.ts +0 -4
  189. package/src/internal/server.ts +190 -60
  190. package/src/internal/session-engine.ts +5 -21
  191. package/src/internal/session-links.ts +88 -0
  192. package/src/internal/sessions-client.ts +1 -0
  193. package/src/types.ts +19 -12
  194. package/templates/agentic-owners/README.md +73 -0
  195. package/templates/agentic-owners/agent/agent.ts +15 -0
  196. package/templates/agentic-owners/agent/channels/github.ts +85 -0
  197. package/templates/agentic-owners/agent/instructions.md +34 -0
  198. package/templates/agentic-owners/agent/lib/config.ts +13 -0
  199. package/templates/agentic-owners/agent/lib/github.ts +241 -0
  200. package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
  201. package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
  202. package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
  203. package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
  204. package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
  205. package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
  206. package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
  207. package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
  208. package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
  209. package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
  210. package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
  211. package/templates/agentic-owners/agent/lib/review.ts +777 -0
  212. package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
  213. package/templates/agentic-owners/evals/evals.config.ts +5 -0
  214. package/templates/agentic-owners/evals/owners.eval.ts +116 -0
  215. package/templates/agentic-owners/package.json +20 -0
  216. package/templates/agentic-owners/tsconfig.json +12 -0
  217. package/templates/demo/README.md +33 -0
  218. package/templates/demo/agent/agent.ts +15 -0
  219. package/templates/demo/agent/channels/github.ts +50 -0
  220. package/templates/demo/agent/channels/queue.ts +30 -0
  221. package/templates/demo/agent/instructions.md +8 -0
  222. package/templates/demo/agent/lib/collect.ts +78 -0
  223. package/templates/demo/agent/tools/record_demo.ts +40 -0
  224. package/templates/demo/evals/demo.eval.ts +33 -0
  225. package/templates/demo/evals/evals.config.ts +5 -0
  226. package/templates/demo/package.json +18 -0
  227. package/templates/demo/tsconfig.json +12 -0
  228. package/templates/pr-autofixer/README.md +44 -0
  229. package/templates/pr-autofixer/agent/agent.ts +22 -0
  230. package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
  231. package/templates/pr-autofixer/agent/channels/github.ts +167 -0
  232. package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
  233. package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
  234. package/templates/pr-autofixer/agent/instructions.md +83 -0
  235. package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
  236. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
  237. package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
  238. package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
  239. package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
  240. package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
  241. package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
  242. package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
  243. package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
  244. package/templates/pr-autofixer/agent/storage.ts +7 -0
  245. package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
  246. package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
  247. package/templates/pr-autofixer/evals/evals.config.ts +5 -0
  248. package/templates/pr-autofixer/init.json +25 -0
  249. package/templates/pr-autofixer/package.json +18 -0
  250. package/templates/pr-autofixer/tsconfig.json +12 -0
  251. package/templates/security-reviewer/README.md +31 -0
  252. package/templates/security-reviewer/agent/agent.ts +15 -0
  253. package/templates/security-reviewer/agent/channels/github.ts +46 -0
  254. package/templates/security-reviewer/agent/instructions.md +22 -0
  255. package/templates/security-reviewer/agent/lib/repos.ts +5 -0
  256. package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
  257. package/templates/security-reviewer/evals/evals.config.ts +5 -0
  258. package/templates/security-reviewer/evals/review.eval.ts +53 -0
  259. package/templates/security-reviewer/init.json +25 -0
  260. package/templates/security-reviewer/package.json +18 -0
  261. package/templates/security-reviewer/tsconfig.json +12 -0
  262. package/dist/docs/assets/chunks/@localSearchIndexroot.BFpWSo6D.js +0 -1
  263. package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
  264. package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
  265. 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
 
@@ -91,7 +91,7 @@ export type CursorScmWorkflowRunMeta = CursorScmCiBaseMeta & {
91
91
  workflowId?: number;
92
92
  };
93
93
 
94
- /** One event delivered by `GET /v0/scm-events` (metadata only). */
94
+ /** One event delivered by `GET /v0/scm-events`. */
95
95
  export interface CursorScmEvent {
96
96
  offset: string;
97
97
  provider: string;
@@ -99,6 +99,8 @@ export interface CursorScmEvent {
99
99
  deliveryId?: string;
100
100
  repo: string;
101
101
  receivedAtMs: number;
102
+ /** Verified webhook body when the API stored it. */
103
+ payload?: unknown;
102
104
  /** Webhook `action` when present. */
103
105
  action?: string;
104
106
  /** PR number when the delivery is PR-scoped. */
@@ -115,6 +117,12 @@ export interface CursorScmEvent {
115
117
  * Zod-valid `issue_comment` / `pull_request_review_comment` webhook body.
116
118
  */
117
119
  comment?: CursorScmCommentMeta;
120
+ /**
121
+ * Origin comment snapshot. Ids are Origin string ids, not GitHub integers.
122
+ */
123
+ originComment?: CursorScmOriginCommentMeta;
124
+ /** Origin actor (author / pusher / commenter) when present. */
125
+ originActor?: CursorScmOriginActorMeta;
118
126
  /** `check_run` snapshot; enough to stub `onCheckRun`. */
119
127
  checkRun?: CursorScmCheckRunMeta;
120
128
  /** `check_suite` snapshot; enough to stub `onCheckSuite`. */
@@ -125,6 +133,25 @@ export interface CursorScmEvent {
125
133
  occurredAtMs?: number;
126
134
  }
127
135
 
136
+ export type CursorScmOriginActorMeta = {
137
+ id: string;
138
+ kind?: string;
139
+ };
140
+
141
+ export type CursorScmOriginCommentMeta = {
142
+ id: string;
143
+ body?: string;
144
+ author?: CursorScmOriginActorMeta;
145
+ };
146
+
147
+ export type CursorScmProvider = "github" | "origin";
148
+
149
+ export interface CursorScmSubscription {
150
+ provider: CursorScmProvider;
151
+ repo: string;
152
+ events: readonly string[];
153
+ }
154
+
128
155
  interface SubscriptionStatus {
129
156
  provider: string;
130
157
  repo: string;
@@ -135,8 +162,17 @@ interface SubscriptionStatus {
135
162
  export interface CursorEventRelayOptions {
136
163
  baseUrl: string;
137
164
  apiKey: string;
138
- repos: readonly string[];
139
- events: readonly string[];
165
+ /**
166
+ * GitHub-only shorthand. Ignored when {@link subscriptions} is set.
167
+ * Existing `--cursor-events --repo` hosts keep passing these.
168
+ */
169
+ repos?: readonly string[];
170
+ events?: readonly string[];
171
+ /**
172
+ * Mixed GitHub + Origin registrations. Provider is part of the
173
+ * subscription key: mirrored `owner/name` slugs must not collide.
174
+ */
175
+ subscriptions?: readonly CursorScmSubscription[];
140
176
  stateDir: string;
141
177
  /**
142
178
  * Called for each matching event; failures retry a bounded number of
@@ -201,6 +237,11 @@ function readDispatchError(body: unknown): string | undefined {
201
237
  return typeof error === "string" && error !== "" ? error : undefined;
202
238
  }
203
239
 
240
+ /** Stored webhook body, or a metadata stub for older stream entries. */
241
+ export function scmEventToGitHubPayload(event: CursorScmEvent): unknown {
242
+ return event.payload ?? scmMetadataToGitHubPayload(event);
243
+ }
244
+
204
245
  /**
205
246
  * Minimal GitHub webhook body from an SCM-events metadata envelope.
206
247
  * When `pullRequest` has title + head/base refs+shas, emits a Zod-valid
@@ -430,30 +471,149 @@ function stubGitHubWorkflowRun(
430
471
  }
431
472
 
432
473
  /**
433
- * GitHub SCM-events relay: a thin {@link CursorRelayCore} adapter. Consumers
474
+ * Origin webhook envelope from an SCM-events metadata record. One parser
475
+ * handles both live Origin deliveries and this stub: `{ deliveryId, event }`.
476
+ */
477
+ export function scmMetadataToOriginPayload(event: {
478
+ repo: string;
479
+ eventName: string;
480
+ deliveryId?: string;
481
+ action?: string;
482
+ prNumber?: number;
483
+ prUrl?: string;
484
+ pullRequest?: CursorScmPullRequestMeta;
485
+ originComment?: CursorScmOriginCommentMeta;
486
+ originActor?: CursorScmOriginActorMeta;
487
+ occurredAtMs?: number;
488
+ }): Record<string, unknown> {
489
+ const slash = event.repo.indexOf("/");
490
+ const owner = slash === -1 ? "" : event.repo.slice(0, slash);
491
+ const name = slash === -1 ? event.repo : event.repo.slice(slash + 1);
492
+ const prMeta = event.pullRequest;
493
+ const prNumber = prMeta?.number ?? event.prNumber;
494
+ const prUrl =
495
+ prMeta?.url ??
496
+ event.prUrl ??
497
+ (prNumber !== undefined
498
+ ? `https://cursor.com/codebase/${owner}/${name}/pull/${prNumber}`
499
+ : undefined);
500
+
501
+ const payload: Record<string, unknown> = {
502
+ repository: {
503
+ name,
504
+ owner: { slug: owner },
505
+ },
506
+ };
507
+
508
+ if (prNumber !== undefined) {
509
+ const pullRequest: Record<string, unknown> = {
510
+ number: prNumber,
511
+ url: prUrl,
512
+ };
513
+ if (prMeta?.title !== undefined) pullRequest.title = prMeta.title;
514
+ if (prMeta?.body !== undefined) pullRequest.body = prMeta.body;
515
+ if (prMeta?.draft !== undefined) pullRequest.draft = prMeta.draft;
516
+ if (prMeta?.merged !== undefined) pullRequest.merged = prMeta.merged;
517
+ if (prMeta?.headRef !== undefined || prMeta?.headSha !== undefined) {
518
+ pullRequest.head = {
519
+ ref: prMeta.headRef,
520
+ sha: prMeta.headSha,
521
+ };
522
+ }
523
+ if (prMeta?.baseRef !== undefined || prMeta?.baseSha !== undefined) {
524
+ pullRequest.base = {
525
+ ref: prMeta.baseRef,
526
+ sha: prMeta.baseSha,
527
+ };
528
+ }
529
+ if (prMeta?.authorLogin !== undefined) {
530
+ pullRequest.author = { id: prMeta.authorLogin };
531
+ }
532
+ payload.pullRequest = pullRequest;
533
+ }
534
+
535
+ const commentMeta = event.originComment;
536
+ if (commentMeta !== undefined) {
537
+ const comment: Record<string, unknown> = { id: commentMeta.id };
538
+ if (commentMeta.body !== undefined) comment.body = commentMeta.body;
539
+ if (commentMeta.author !== undefined) comment.author = commentMeta.author;
540
+ payload.comment = comment;
541
+ }
542
+ if (event.originActor !== undefined) {
543
+ payload.actor = event.originActor;
544
+ }
545
+
546
+ return {
547
+ deliveryId: event.deliveryId,
548
+ event: {
549
+ type: event.eventName,
550
+ ...(event.occurredAtMs === undefined
551
+ ? {}
552
+ : { eventTime: new Date(event.occurredAtMs).toISOString() }),
553
+ payload,
554
+ },
555
+ };
556
+ }
557
+
558
+ function subscriptionKey(provider: string, repo: string): string {
559
+ return `${provider}:${repo.toLowerCase()}`;
560
+ }
561
+
562
+ function subscriptionLabel(provider: string, repo: string): string {
563
+ return provider === "github" ? repo : `${provider}:${repo}`;
564
+ }
565
+
566
+ export function resolveCursorScmSubscriptions(
567
+ options: Pick<CursorEventRelayOptions, "repos" | "events" | "subscriptions">
568
+ ): CursorScmSubscription[] {
569
+ if (options.subscriptions !== undefined) {
570
+ return options.subscriptions.map((subscription) => ({
571
+ provider: subscription.provider,
572
+ repo: subscription.repo.trim().toLowerCase(),
573
+ events: [...subscription.events],
574
+ }));
575
+ }
576
+ const events = [...(options.events ?? [])];
577
+ return (options.repos ?? []).map((repo) => ({
578
+ provider: "github" as const,
579
+ repo: repo.trim().toLowerCase(),
580
+ events,
581
+ }));
582
+ }
583
+
584
+ /**
585
+ * SCM-events relay: a thin {@link CursorRelayCore} adapter. Consumers
434
586
  * keep the same `new CursorEventRelay(options).start()/.stop()` surface.
435
587
  */
436
588
  export class CursorEventRelay {
437
589
  private readonly core: CursorRelayCore<CursorScmEvent>;
590
+ private readonly subscriptions: CursorScmSubscription[];
438
591
  /**
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.
592
+ * Confirmed `provider:repo` keys for THIS consumer, with the event kinds
593
+ * requested for each. The stream is shared with sibling consumers, so
594
+ * GitHub and Origin deliveries for the same owner/name must not mix.
442
595
  */
443
- private subscribedRepos = new Set<string>();
444
- private readonly loggedSkippedRepos = new Set<string>();
596
+ private subscribedEvents = new Map<string, ReadonlySet<string>>();
597
+ private readonly loggedSkippedKeys = new Set<string>();
445
598
 
446
599
  constructor(private readonly options: CursorEventRelayOptions) {
600
+ this.subscriptions = resolveCursorScmSubscriptions(options);
601
+ const requestedEvents = new Map(
602
+ this.subscriptions.map((subscription) => [
603
+ subscriptionKey(subscription.provider, subscription.repo),
604
+ new Set(subscription.events),
605
+ ])
606
+ );
447
607
  const adapter: RelayAdapter<CursorScmEvent> = {
448
608
  logPrefix: "cursor-events",
449
609
  subscribePath: "/v0/scm-events/subscriptions",
450
610
  pollPath: "/v0/scm-events",
451
611
  buildRegistrationBody: (consumerId) => ({
452
612
  consumerId,
453
- subscriptions: this.options.repos.map((repo) => ({
454
- provider: "github",
455
- repo,
456
- events: [...this.options.events],
613
+ subscriptions: this.subscriptions.map((subscription) => ({
614
+ provider: subscription.provider,
615
+ repo: subscription.repo,
616
+ events: [...subscription.events],
457
617
  })),
458
618
  }),
459
619
  // Empty subscriptions drop this consumer's registration on clean stop
@@ -465,20 +625,27 @@ export class CursorEventRelay {
465
625
  applyRegistrationResponse: (json) => {
466
626
  const result = json as { repos?: SubscriptionStatus[] };
467
627
  const statuses = result.repos ?? [];
468
- this.subscribedRepos = new Set(
469
- statuses
470
- .filter((status) => status.isSubscribed)
471
- .map((status) => status.repo)
472
- );
628
+ this.subscribedEvents = new Map();
473
629
  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
- );
630
+ const confirmed: string[] = [];
631
+ for (const status of statuses) {
632
+ const provider = status.provider || "github";
633
+ const key = subscriptionKey(provider, status.repo);
634
+ if (!status.isSubscribed) {
635
+ lines.push(
636
+ `not subscribed to ${subscriptionLabel(provider, status.repo)}: ${status.detail ?? "unknown reason"}`
637
+ );
638
+ continue;
639
+ }
640
+ this.subscribedEvents.set(key, requestedEvents.get(key) ?? new Set());
641
+ confirmed.push(subscriptionLabel(provider, status.repo));
478
642
  }
479
- if (this.subscribedRepos.size > 0) {
643
+ if (confirmed.length > 0) {
644
+ const eventKinds = [
645
+ ...new Set(this.subscriptions.flatMap((s) => [...s.events])),
646
+ ];
480
647
  lines.push(
481
- `subscribed to ${[...this.subscribedRepos].join(", ")} for events: ${this.options.events.join(", ")}`
648
+ `subscribed to ${confirmed.join(", ")} for events: ${eventKinds.join(", ")}`
482
649
  );
483
650
  }
484
651
  return lines;
@@ -503,21 +670,23 @@ export class CursorEventRelay {
503
670
 
504
671
  /**
505
672
  * 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.
673
+ * provider+repo must be one the server confirmed for us and the event kind
674
+ * one that subscription declared. GitHub and Origin share owner/name slugs
675
+ * on mirrors, so the provider is part of the key.
509
676
  */
510
677
  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);
678
+ const key = subscriptionKey(event.provider || "github", event.repo);
679
+ const events = this.subscribedEvents.get(key);
680
+ if (events === undefined) {
681
+ if (!this.loggedSkippedKeys.has(key)) {
682
+ this.loggedSkippedKeys.add(key);
514
683
  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)`
684
+ `[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
685
  );
517
686
  }
518
687
  return false;
519
688
  }
520
- return this.options.events.includes(event.eventName);
689
+ return events.has(event.eventName);
521
690
  }
522
691
 
523
692
  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
+ }