@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
@@ -17,7 +17,6 @@ import { join } from "node:path";
17
17
  import {
18
18
  type CursorGithubRepoScope,
19
19
  createCursorAccountGithubTokenResolver,
20
- cursorGithubPermissionsForChannel,
21
20
  type GithubCursorAccountRuntime,
22
21
  getGithubCursorAccountRuntime,
23
22
  parseCursorGithubRepoScope,
@@ -25,6 +24,12 @@ import {
25
24
  resolveCursorGithubProxyUrl,
26
25
  } from "../channels/github/cursor-account.js";
27
26
  import { findGitHubChannelTargets } from "../channels/github/forward.js";
27
+ import {
28
+ getOriginCursorAccountRuntime,
29
+ type OriginCursorAccountRuntime,
30
+ parseCursorOriginRepoScope,
31
+ } from "../channels/origin/cursor-account.js";
32
+ import { findOriginChannelTargets } from "../channels/origin/origin-channel.js";
28
33
  import {
29
34
  dispatchCursorSlackEnvelope,
30
35
  getSlackCursorAccountRuntime,
@@ -59,9 +64,11 @@ import {
59
64
  import { CursorGithubCredentialProvider } from "./cursor/github-credentials.js";
60
65
  import {
61
66
  CursorEventRelay,
67
+ type CursorScmSubscription,
62
68
  formatCursorScmDispatchOutcome,
63
69
  formatCursorScmEventLog,
64
70
  scmMetadataToGitHubPayload,
71
+ scmMetadataToOriginPayload,
65
72
  } from "./cursor-event-relay.js";
66
73
  import {
67
74
  type CursorSlackAgentRegistration,
@@ -89,6 +96,7 @@ import {
89
96
  } from "./eval-run-store.js";
90
97
  import {
91
98
  collectGitHubTriggerTargets,
99
+ collectOriginTriggerTargets,
92
100
  handleAgentServeTrigger,
93
101
  } from "./handleAgentServeTrigger.js";
94
102
  import { createHostPlatforms, warmupHostGitHubAuth } from "./host-platforms.js";
@@ -151,11 +159,7 @@ import {
151
159
  } from "./resolved-connections.js";
152
160
  import { Router } from "./router.js";
153
161
  import { ScheduleRunner, UnknownScheduleError } from "./schedule-runner.js";
154
- import {
155
- type AgentRunner,
156
- CursorSdkRunner,
157
- resolveServeWorkspaceScanCacheTtlMs,
158
- } from "./sdk-runner.js";
162
+ import { type AgentRunner, CursorSdkRunner } from "./sdk-runner.js";
159
163
  import {
160
164
  SessionAuthError,
161
165
  SessionBusyError,
@@ -267,11 +271,20 @@ export async function startServer(
267
271
  }
268
272
 
269
273
  const cursorGithubTargets = collectCursorGithubTargets(mounts);
274
+ const cursorOriginTargets = collectCursorOriginTargets(mounts);
270
275
  const cursorGithubConfig = resolveCursorGithubEventConfig({
271
276
  configured: options.cursorEvents,
272
277
  targets: cursorGithubTargets,
273
278
  });
274
- const effectiveCursorEvents = cursorGithubConfig.cursorEvents;
279
+ const cursorOriginScope = resolveCursorOriginRepoScope({
280
+ explicitRepos: options.cursorEvents?.repos ?? [],
281
+ targets: cursorOriginTargets,
282
+ });
283
+ const effectiveCursorEvents =
284
+ cursorGithubConfig.cursorEvents ??
285
+ (cursorOriginScope === undefined
286
+ ? undefined
287
+ : { repos: [...cursorOriginScope.repos] });
275
288
 
276
289
  // One resolution for every Cursor credential consumer (SDK runner, account
277
290
  // MCP bridge, SCM event relay, GitHub credentials): explicit option →
@@ -379,17 +392,11 @@ export async function startServer(
379
392
  "[agent-sdk] warning: --allow-anonymous admits every HTTP caller as the same principal; use only on a trusted network (e.g. Tailscale) and prefer --bearer-token for shared hosts"
380
393
  );
381
394
  }
382
- const workspaceScanCacheTtlMs = resolveServeWorkspaceScanCacheTtlMs({
383
- dev,
384
- });
385
395
  const runner =
386
396
  options.runner ??
387
397
  new CursorSdkRunner({
388
398
  ...(accountKey === undefined ? {} : { apiKey: accountKey.apiKey }),
389
399
  logger,
390
- ...(workspaceScanCacheTtlMs === undefined
391
- ? {}
392
- : { workspaceScanCacheTtlMs }),
393
400
  });
394
401
 
395
402
  const router = new Router();
@@ -684,7 +691,7 @@ export async function startServer(
684
691
  // job (backend `/github_webhook` already verified); we only admit loopback
685
692
  // so this route is not a public unsigned endpoint. Agents decide whether
686
693
  // to run via their onPullRequest / onComment / … hooks.
687
- const collectTriggerTargets = () =>
694
+ const collectGitHubTargets = () =>
688
695
  collectGitHubTriggerTargets({
689
696
  mounts: runtimes.map((runtime) => ({
690
697
  slug: runtime.slug,
@@ -693,6 +700,15 @@ export async function startServer(
693
700
  buildChannelHandlerArgs({ runtime, channelId }),
694
701
  })),
695
702
  });
703
+ const collectOriginTargets = () =>
704
+ collectOriginTriggerTargets({
705
+ mounts: runtimes.map((runtime) => ({
706
+ slug: runtime.slug,
707
+ channels: runtime.project.channels,
708
+ buildHandlerArgs: (channelId) =>
709
+ buildChannelHandlerArgs({ runtime, channelId }),
710
+ })),
711
+ });
696
712
  router.add({
697
713
  method: "POST",
698
714
  pattern: "/v1/webhooks/github",
@@ -704,7 +720,7 @@ export async function startServer(
704
720
  bodyText,
705
721
  eventName: request.headers.get("x-github-event"),
706
722
  deliveryId: request.headers.get("x-github-delivery") ?? undefined,
707
- targets: collectTriggerTargets(),
723
+ targets: collectGitHubTargets(),
708
724
  });
709
725
  const status = result.ok ? 202 : (result.targets[0]?.status ?? 500);
710
726
  return jsonResponse(result, { status });
@@ -743,13 +759,17 @@ export async function startServer(
743
759
  // Cursor SCM events relay (opt-in: serve({ cursorEvents }) / --cursor-events).
744
760
  const cursorEventRelay = startCursorEventRelay({
745
761
  config: effectiveCursorEvents,
762
+ githubRepos: cursorGithubConfig.cursorEvents?.repos ?? [],
763
+ originRepos: cursorOriginScope?.repos ?? [],
746
764
  stateRoot: options.stateRoot,
747
765
  mounts,
748
766
  auth: cursorEventsAuth,
749
767
  logger,
750
- collectTriggerTargets,
768
+ collectGitHubTargets,
769
+ collectOriginTargets,
751
770
  cursorAccountOnly:
752
- options.cursorEvents === undefined && cursorGithubTargets.length > 0,
771
+ options.cursorEvents === undefined &&
772
+ (cursorGithubTargets.length > 0 || cursorOriginTargets.length > 0),
753
773
  });
754
774
 
755
775
  const server = createServer((req, res) => {
@@ -1759,37 +1779,73 @@ function requireCursorEventsAuth(
1759
1779
  /**
1760
1780
  * Opt-in Cursor SCM events relay. Credentials are already required by
1761
1781
  * {@link requireCursorEventsAuth}; the remaining unusable configs (no repos,
1762
- * no github channel) warn and leave the relay off — serve still starts.
1782
+ * no github/origin channel) warn and leave the relay off — serve still starts.
1783
+ *
1784
+ * GitHub and Origin subscriptions are registered independently so mirrored
1785
+ * `owner/name` slugs do not mix. `--cursor-events --repo` feeds GitHub
1786
+ * channels; Origin is subscribed only when an `originChannel({ cursorAccount })`
1787
+ * opted in.
1763
1788
  */
1764
1789
  function startCursorEventRelay(input: {
1765
1790
  config: ServeOptions["cursorEvents"];
1791
+ githubRepos: readonly string[];
1792
+ originRepos: readonly string[];
1766
1793
  stateRoot: string | undefined;
1767
1794
  mounts: AgentMount[];
1768
1795
  auth: CursorEventsAuth | undefined;
1769
1796
  logger: (line: string) => void;
1770
- collectTriggerTargets: () => ReturnType<typeof collectGitHubTriggerTargets>;
1797
+ collectGitHubTargets: () => ReturnType<typeof collectGitHubTriggerTargets>;
1798
+ collectOriginTargets: () => ReturnType<typeof collectOriginTriggerTargets>;
1771
1799
  cursorAccountOnly: boolean;
1772
1800
  }): CursorEventRelay | undefined {
1773
- const { config, stateRoot, mounts, auth, logger, collectTriggerTargets } =
1774
- input;
1801
+ const { config, stateRoot, mounts, auth, logger } = input;
1775
1802
  if (config === undefined || auth === undefined) {
1776
1803
  return undefined;
1777
1804
  }
1778
- const repos = [...new Set(config.repos.map((repo) => repo.trim()))].filter(
1779
- (repo) => repo.length > 0
1780
- );
1781
- if (repos.length === 0) {
1782
- logger(
1783
- "[agent-sdk] cursor-events: no repos configured (pass --repo owner/name); relay disabled"
1784
- );
1785
- return undefined;
1786
- }
1787
1805
  const githubTargets = findGitHubChannelTargets(mounts);
1788
- const events = [...new Set(githubTargets.flatMap((target) => target.events))];
1789
- if (events.length === 0) {
1790
- logger(
1791
- "[agent-sdk] cursor-events: no mounted agent declares a github channel; relay disabled"
1792
- );
1806
+ const originTargets = findOriginChannelTargets(mounts);
1807
+ const githubEvents = [
1808
+ ...new Set(githubTargets.flatMap((target) => target.events)),
1809
+ ];
1810
+ const originEvents = [
1811
+ ...new Set(originTargets.flatMap((target) => target.events)),
1812
+ ];
1813
+ const githubRepos = [
1814
+ ...new Set(input.githubRepos.map((repo) => repo.trim().toLowerCase())),
1815
+ ].filter((repo) => repo.length > 0);
1816
+ const originRepos = [
1817
+ ...new Set(input.originRepos.map((repo) => repo.trim().toLowerCase())),
1818
+ ].filter((repo) => repo.length > 0);
1819
+
1820
+ const subscriptions: CursorScmSubscription[] = [];
1821
+ if (githubEvents.length > 0 && githubRepos.length > 0) {
1822
+ for (const repo of githubRepos) {
1823
+ subscriptions.push({
1824
+ provider: "github",
1825
+ repo,
1826
+ events: githubEvents,
1827
+ });
1828
+ }
1829
+ }
1830
+ if (originEvents.length > 0 && originRepos.length > 0) {
1831
+ for (const repo of originRepos) {
1832
+ subscriptions.push({
1833
+ provider: "origin",
1834
+ repo,
1835
+ events: originEvents,
1836
+ });
1837
+ }
1838
+ }
1839
+ if (subscriptions.length === 0) {
1840
+ if (githubRepos.length === 0 && originRepos.length === 0) {
1841
+ logger(
1842
+ "[agent-sdk] cursor-events: no repos configured (pass --repo owner/name); relay disabled"
1843
+ );
1844
+ } else {
1845
+ logger(
1846
+ "[agent-sdk] cursor-events: no mounted agent declares a github or origin channel; relay disabled"
1847
+ );
1848
+ }
1793
1849
  return undefined;
1794
1850
  }
1795
1851
  const baseStateRoot =
@@ -1798,15 +1854,36 @@ function startCursorEventRelay(input: {
1798
1854
  const relay = new CursorEventRelay({
1799
1855
  baseUrl: auth.baseUrl,
1800
1856
  apiKey: auth.apiKey,
1801
- repos,
1802
- events,
1857
+ subscriptions,
1803
1858
  stateDir: join(baseStateRoot, "cursor-events"),
1804
1859
  logger,
1805
- // Stream is compact metadata (Sand-style); stub a GitHub body from the
1806
- // envelope — rich pullRequest meta when present for onPullRequest Zod.
1807
1860
  dispatch: async (event) => {
1861
+ if (event.provider === "origin") {
1862
+ const targets = input.collectOriginTargets().filter((target) => {
1863
+ const cursorAccount = target.options.cursorAccount;
1864
+ if (
1865
+ input.cursorAccountOnly &&
1866
+ cursorAccount !== true &&
1867
+ typeof cursorAccount !== "object"
1868
+ ) {
1869
+ return false;
1870
+ }
1871
+ return typeof cursorAccount !== "object"
1872
+ ? true
1873
+ : cursorAccount.repos.some(
1874
+ (repo) => repo.toLowerCase() === event.repo.toLowerCase()
1875
+ );
1876
+ });
1877
+ const result = await handleAgentServeTrigger({
1878
+ type: "origin",
1879
+ bodyText: JSON.stringify(scmMetadataToOriginPayload(event)),
1880
+ targets,
1881
+ });
1882
+ logCursorScmDispatch(logger, event, result);
1883
+ return;
1884
+ }
1808
1885
  if (event.provider !== "github") return;
1809
- const targets = collectTriggerTargets().filter((target) => {
1886
+ const targets = input.collectGitHubTargets().filter((target) => {
1810
1887
  const cursorAccount = target.options.cursorAccount;
1811
1888
  if (
1812
1889
  input.cursorAccountOnly &&
@@ -1828,27 +1905,35 @@ function startCursorEventRelay(input: {
1828
1905
  deliveryId: event.deliveryId,
1829
1906
  targets,
1830
1907
  });
1831
- const outcomes = result.targets
1832
- .map((target) => formatCursorScmDispatchOutcome(target))
1833
- .join(", ");
1834
- const rejected = result.targets.filter((target) => target.status >= 400);
1835
- if (rejected.length > 0) {
1836
- logger(
1837
- `[agent-sdk] cursor-events: dropped ${formatCursorScmEventLog(event)} before hook: ${rejected.map(formatCursorScmDispatchOutcome).join(", ")}`
1838
- );
1839
- }
1840
- logger(
1841
- `[agent-sdk] cursor-events: dispatched ${formatCursorScmEventLog(event)}${outcomes === "" ? "" : ` → ${outcomes}`}`
1842
- );
1908
+ logCursorScmDispatch(logger, event, result);
1843
1909
  },
1844
1910
  });
1845
1911
  relay.start();
1846
1912
  logger(
1847
- `[agent-sdk] cursor-events: relaying ${events.length} event kind(s) for ${repos.join(", ")} from ${auth.baseUrl}`
1913
+ `[agent-sdk] cursor-events: relaying ${subscriptions.length} subscription(s) from ${auth.baseUrl}`
1848
1914
  );
1849
1915
  return relay;
1850
1916
  }
1851
1917
 
1918
+ function logCursorScmDispatch(
1919
+ logger: (line: string) => void,
1920
+ event: Parameters<typeof formatCursorScmEventLog>[0],
1921
+ result: Awaited<ReturnType<typeof handleAgentServeTrigger>>
1922
+ ): void {
1923
+ const outcomes = result.targets
1924
+ .map((target) => formatCursorScmDispatchOutcome(target))
1925
+ .join(", ");
1926
+ const rejected = result.targets.filter((target) => target.status >= 400);
1927
+ if (rejected.length > 0) {
1928
+ logger(
1929
+ `[agent-sdk] cursor-events: dropped ${formatCursorScmEventLog(event)} before hook: ${rejected.map(formatCursorScmDispatchOutcome).join(", ")}`
1930
+ );
1931
+ }
1932
+ logger(
1933
+ `[agent-sdk] cursor-events: dispatched ${formatCursorScmEventLog(event)}${outcomes === "" ? "" : ` → ${outcomes}`}`
1934
+ );
1935
+ }
1936
+
1852
1937
  interface CursorGithubTarget {
1853
1938
  slug: string;
1854
1939
  channelId: string;
@@ -1893,11 +1978,8 @@ function resolveCursorGithubEventConfig(input: {
1893
1978
  ]),
1894
1979
  ];
1895
1980
  const permissions = resolveCursorGithubPermissions(
1896
- input.targets.map((target) =>
1897
- cursorGithubPermissionsForChannel(
1898
- target.accountRuntime.options,
1899
- target.accountRuntime.scope?.permissions
1900
- )
1981
+ input.targets.map(
1982
+ (target) => target.accountRuntime.scope?.permissions ?? "pr-write"
1901
1983
  )
1902
1984
  );
1903
1985
  const scope = parseCursorGithubRepoScope(repos, permissions);
@@ -1910,6 +1992,54 @@ function resolveCursorGithubEventConfig(input: {
1910
1992
  };
1911
1993
  }
1912
1994
 
1995
+ interface CursorOriginTarget {
1996
+ slug: string;
1997
+ channelId: string;
1998
+ accountRuntime: OriginCursorAccountRuntime;
1999
+ }
2000
+
2001
+ function collectCursorOriginTargets(
2002
+ mounts: AgentMount[]
2003
+ ): CursorOriginTarget[] {
2004
+ const targets: CursorOriginTarget[] = [];
2005
+ for (const mount of mounts) {
2006
+ for (const channel of mount.project.channels) {
2007
+ const accountRuntime = getOriginCursorAccountRuntime(channel.definition);
2008
+ if (accountRuntime !== undefined) {
2009
+ targets.push({
2010
+ slug: mount.slug,
2011
+ channelId: channel.id,
2012
+ accountRuntime,
2013
+ });
2014
+ }
2015
+ }
2016
+ }
2017
+ return targets;
2018
+ }
2019
+
2020
+ /**
2021
+ * Origin cursorAccount repos. `true` takes the host's explicit `--repo`
2022
+ * list (not GitHub channel-declared repos). `{ repos }` is self-contained.
2023
+ */
2024
+ function resolveCursorOriginRepoScope(input: {
2025
+ explicitRepos: readonly string[];
2026
+ targets: CursorOriginTarget[];
2027
+ }): ReturnType<typeof parseCursorOriginRepoScope> | undefined {
2028
+ if (input.targets.length === 0) {
2029
+ return undefined;
2030
+ }
2031
+ const wantsHostRepos = input.targets.some(
2032
+ (target) => target.accountRuntime.options.cursorAccount === true
2033
+ );
2034
+ const repos = [
2035
+ ...(wantsHostRepos ? input.explicitRepos : []),
2036
+ ...input.targets.flatMap(
2037
+ (target) => target.accountRuntime.scope?.repos ?? []
2038
+ ),
2039
+ ];
2040
+ return parseCursorOriginRepoScope(repos);
2041
+ }
2042
+
1913
2043
  interface CursorSlackTarget {
1914
2044
  runtime: MountedRuntime;
1915
2045
  channelId: string;
@@ -17,7 +17,6 @@ import {
17
17
  resolveABMaxPlaygroundSessions,
18
18
  resolveABSplit,
19
19
  } from "../ab.js";
20
- import { slackThreadPermalink } from "../channels/slack/links.js";
21
20
  import {
22
21
  SlackAskDedupeStore,
23
22
  SlackNudgeStore,
@@ -122,6 +121,7 @@ import {
122
121
  } from "./prompt-context.js";
123
122
  import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections.js";
124
123
  import type { AgentRunner } from "./sdk-runner.js";
124
+ import { sessionExternalLinks } from "./session-links.js";
125
125
  import { SessionStore } from "./session-store.js";
126
126
  import { StorageCoordinator } from "./storage-coordinator.js";
127
127
  import type { FrameworkSessionStore } from "./storage-roles.js";
@@ -2759,12 +2759,12 @@ export class SessionEngine {
2759
2759
  return records
2760
2760
  .filter((record) => includeAll || samePrincipal(record.auth, caller))
2761
2761
  .map((record) => {
2762
- const externalUrl = externalUrlForSession(record);
2762
+ const links = sessionExternalLinks(record);
2763
2763
  return {
2764
2764
  sessionId: record.sessionId,
2765
2765
  channelId: record.channelId,
2766
2766
  mode: record.mode,
2767
- ...(record.title === undefined ? {} : { title: record.title }),
2767
+ title: record.title,
2768
2768
  turnCount: record.turnCount,
2769
2769
  running:
2770
2770
  this.activeTurns.has(record.sessionId) ||
@@ -2781,7 +2781,8 @@ export class SessionEngine {
2781
2781
  record.channelId,
2782
2782
  record.continuationKey
2783
2783
  ),
2784
- ...(externalUrl === undefined ? {} : { externalUrl }),
2784
+ externalUrl: links.slack ?? links.github,
2785
+ prUrl: links.github,
2785
2786
  };
2786
2787
  });
2787
2788
  }
@@ -3643,23 +3644,6 @@ function approvalResolverLabel(auth: AuthContext | null): string | undefined {
3643
3644
  return auth.principalId;
3644
3645
  }
3645
3646
 
3646
- /** Deep link to the external conversation (Slack thread today). */
3647
- function externalUrlForSession(record: SessionRecord): string | undefined {
3648
- const auth = record.auth;
3649
- if (auth === null || auth.authenticator !== "slack") {
3650
- return undefined;
3651
- }
3652
- const channelId = auth.attributes?.channel_id ?? "";
3653
- const threadTs = auth.attributes?.thread_ts ?? "";
3654
- const teamId = auth.attributes?.team_id;
3655
- const url = slackThreadPermalink({
3656
- channelId,
3657
- threadTs,
3658
- ...(teamId === undefined ? {} : { teamId }),
3659
- });
3660
- return url ?? undefined;
3661
- }
3662
-
3663
3647
  /** Let the HTTP NDJSON writer flush before the next SDK update. */
3664
3648
  function yieldEventLoop(): Promise<void> {
3665
3649
  return new Promise((resolve) => {
@@ -0,0 +1,88 @@
1
+ import { parseGitHubPrRef } from "../channels/github/replay.js";
2
+ import { slackThreadPermalink } from "../channels/slack/links.js";
3
+ import { extractGithubPrRef } from "../channels/slack/pr-ref.js";
4
+ import type { AgentCloudOptions, AuthContext } from "../types.js";
5
+
6
+ export type SessionExternalLinks = {
7
+ slack: string | undefined;
8
+ github: string | undefined;
9
+ };
10
+
11
+ type SessionLinkSource = {
12
+ auth: AuthContext | null;
13
+ title?: string;
14
+ continuationKey: string | null;
15
+ cloudOverride?: AgentCloudOptions;
16
+ };
17
+
18
+ const SLACK_TITLE = /^slack:([CGD][A-Z0-9]+):(\d+\.\d+)$/i;
19
+ const SLACK_CONTINUATION = /^([CGD][A-Z0-9]+):(\d+\.\d+)$/i;
20
+
21
+ export function sessionExternalLinks(
22
+ record: SessionLinkSource
23
+ ): SessionExternalLinks {
24
+ return {
25
+ slack: slackUrl(record),
26
+ github: githubUrl(record),
27
+ };
28
+ }
29
+
30
+ function slackUrl(record: SessionLinkSource): string | undefined {
31
+ const auth = record.auth;
32
+ if (auth !== null && auth.authenticator === "slack") {
33
+ const fromAuth = slackThreadPermalink({
34
+ channelId: auth.attributes?.channel_id ?? "",
35
+ threadTs: auth.attributes?.thread_ts ?? "",
36
+ teamId: auth.attributes?.team_id,
37
+ });
38
+ if (fromAuth !== null) {
39
+ return fromAuth;
40
+ }
41
+ }
42
+ const fromTitle = (record.title ?? "").trim().match(SLACK_TITLE);
43
+ if (fromTitle !== null) {
44
+ return (
45
+ slackThreadPermalink({
46
+ channelId: fromTitle[1] as string,
47
+ threadTs: fromTitle[2] as string,
48
+ }) ?? undefined
49
+ );
50
+ }
51
+ const fromKey = (record.continuationKey ?? "")
52
+ .trim()
53
+ .match(SLACK_CONTINUATION);
54
+ if (fromKey === null) {
55
+ return undefined;
56
+ }
57
+ return (
58
+ slackThreadPermalink({
59
+ channelId: fromKey[1] as string,
60
+ threadTs: fromKey[2] as string,
61
+ }) ?? undefined
62
+ );
63
+ }
64
+
65
+ function githubUrl(record: SessionLinkSource): string | undefined {
66
+ const fromTitle = extractGithubPrRef(record.title ?? "");
67
+ if (fromTitle !== undefined) {
68
+ return fromTitle.url;
69
+ }
70
+ const key = record.continuationKey ?? "";
71
+ if (key.startsWith("github-pr:")) {
72
+ return parseGitHubPrRef(key.slice("github-pr:".length))?.url;
73
+ }
74
+ const repos = record.cloudOverride?.repos;
75
+ if (repos === undefined) {
76
+ return undefined;
77
+ }
78
+ for (const repo of repos) {
79
+ if (repo.prUrl === undefined) {
80
+ continue;
81
+ }
82
+ const parsed = parseGitHubPrRef(repo.prUrl);
83
+ if (parsed !== undefined) {
84
+ return parsed.url;
85
+ }
86
+ }
87
+ return undefined;
88
+ }
@@ -171,6 +171,7 @@ function parseSessionSummary(value: unknown): SessionSummary | undefined {
171
171
  continuationToken,
172
172
  externalUrl:
173
173
  typeof row.externalUrl === "string" ? row.externalUrl : undefined,
174
+ prUrl: typeof row.prUrl === "string" ? row.prUrl : undefined,
174
175
  };
175
176
  }
176
177
 
package/src/types.ts CHANGED
@@ -254,15 +254,14 @@ export interface AgentConfig {
254
254
  * - `"resident"` (default): the in-process session engine owns delivery —
255
255
  * today's behavior, unchanged.
256
256
  * - `"ledger"`: every delivery coordinates through the KV session ledger
257
- * before its turn runs: the event parks durably in the session mailbox
258
- * (the source ack point moves after that write), a one-writer-per-session
259
- * lease with generation fencing serializes turns, and drained batches
260
- * commit through a fenced sequence. A delivery that finds the lease
261
- * held walks away; the holder drains the parked event, and the janitor
262
- * recovers sessions whose holder died. Requires a store with
263
- * conditional writes and prefix listing (the `cas` group on
257
+ * before its turn runs: session identity, a one-writer-per-session
258
+ * lease with generation fencing, and a fenced commit sequence.
259
+ * Requires a store with conditional writes (the `cas` group on
264
260
  * `defineStorage`, or an explicit CAS-capable override); startup fails
265
- * fast otherwise.
261
+ * fast otherwise. This stage is coordination only — there is no
262
+ * durable inbox yet, so an event arriving while the lease is held
263
+ * waits in-process and a crash loses it exactly as resident serving
264
+ * does. The durable mailbox lands in a follow-up.
266
265
  */
267
266
  export type AgentServingMode = "resident" | "ledger";
268
267
 
@@ -1448,11 +1447,11 @@ export interface SessionSummary {
1448
1447
  /** `<channelId>:<key>` wire token, or null when not addressable. */
1449
1448
  continuationToken: string | null;
1450
1449
  /**
1451
- * Optional deep link to the external conversation (e.g. Slack thread).
1452
- * Populated for channel-driven sessions when the principal carries enough
1453
- * attributes to build a permalink.
1450
+ * Conversation permalink (Slack thread, else GitHub PR).
1454
1451
  */
1455
1452
  externalUrl?: string;
1453
+ /** GitHub PR when the session is about one. */
1454
+ prUrl?: string;
1456
1455
  }
1457
1456
 
1458
1457
  /**
@@ -1907,6 +1906,11 @@ export interface ChannelMeta {
1907
1906
  * to dispatch on. Used to derive `gh webhook forward --events`.
1908
1907
  */
1909
1908
  githubWebhookEvents?: readonly string[];
1909
+ /**
1910
+ * For Origin webhook packs: the Origin event types this channel is wired
1911
+ * to dispatch on. Used by `--cursor-events` to subscribe `provider: "origin"`.
1912
+ */
1913
+ originWebhookEvents?: readonly string[];
1910
1914
  /**
1911
1915
  * For Slack packs with the opt-in channel watch: the allowlisted channels
1912
1916
  * (ids or `#names`) this channel dispatches on without a mention. Surfaced
@@ -2549,6 +2553,8 @@ export interface AgentProjectInfo {
2549
2553
  kind?: string;
2550
2554
  /** GitHub webhook events the channel dispatches on, when declared. */
2551
2555
  githubWebhookEvents?: readonly string[];
2556
+ /** Origin webhook event types the channel dispatches on, when declared. */
2557
+ originWebhookEvents?: readonly string[];
2552
2558
  /** Slack channels watched for posts (opt-in channel watch), when declared. */
2553
2559
  slackChannelPosts?: readonly string[];
2554
2560
  }>;
@@ -2746,7 +2752,8 @@ export interface ServeOptions {
2746
2752
  */
2747
2753
  cloudToolsUrl?: string;
2748
2754
  /**
2749
- * Pull GitHub webhook metadata from `/v0/scm-events` (Cursor account auth).
2755
+ * Pull SCM webhook metadata from `/v0/scm-events` (Cursor account auth).
2756
+ * GitHub and Origin subscriptions are registered independently.
2750
2757
  * The stream is read as the host's Cursor user, so this requires a signed-in
2751
2758
  * host: `serve` throws when no key resolves from `apiKey` here,
2752
2759
  * {@link ServeOptions.apiKey}, `CURSOR_API_KEY`, or `agent-serve login`.