@cursor/july 0.1.54 → 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 (293) 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.DAKcDclS.js → app.CW3KJh72.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.BNIaCSfG.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.B47GzJjc.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.-V6J7Cl8.js → reference_cli.md.Cl14Jfo3.js} +10 -2
  45. package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.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/assets/templates_triage.md.DWuQ1bZz.js +9 -0
  57. package/dist/docs/assets/templates_triage.md.DWuQ1bZz.lean.js +1 -0
  58. package/dist/docs/building-with-agents.html +4 -4
  59. package/dist/docs/concepts.html +5 -5
  60. package/dist/docs/deployment.html +4 -4
  61. package/dist/docs/evals.html +4 -4
  62. package/dist/docs/example-agents/approval-buddy.html +4 -4
  63. package/dist/docs/example-agents/benny.html +4 -4
  64. package/dist/docs/example-agents/bugbot.html +4 -4
  65. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  66. package/dist/docs/example-agents/codeowners-review.html +4 -4
  67. package/dist/docs/example-agents/concierge.html +4 -4
  68. package/dist/docs/example-agents/fsd.html +5 -5
  69. package/dist/docs/example-agents/index.html +4 -4
  70. package/dist/docs/example-agents/knowledge-base.html +4 -4
  71. package/dist/docs/example-agents/oncall.html +4 -4
  72. package/dist/docs/example-agents/security-reviewer.html +5 -5
  73. package/dist/docs/example-agents/slack-agent.html +4 -4
  74. package/dist/docs/example-agents/weather-agent.html +4 -4
  75. package/dist/docs/guides/agent-to-agent.html +4 -4
  76. package/dist/docs/guides/cloud-runtime.html +4 -4
  77. package/dist/docs/guides/convert-automation.html +36 -0
  78. package/dist/docs/guides/github.html +7 -7
  79. package/dist/docs/guides/human-in-the-loop.html +4 -4
  80. package/dist/docs/guides/mcp-oauth.html +4 -4
  81. package/dist/docs/guides/opentelemetry.html +4 -4
  82. package/dist/docs/guides/slack.html +4 -4
  83. package/dist/docs/guides/webhooks.html +4 -4
  84. package/dist/docs/hashmap.json +1 -1
  85. package/dist/docs/hillclimbing.html +4 -4
  86. package/dist/docs/index.html +6 -6
  87. package/dist/docs/quickstart.html +4 -4
  88. package/dist/docs/reference/agent-config.html +4 -4
  89. package/dist/docs/reference/artifacts.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +14 -6
  92. package/dist/docs/reference/connections.html +4 -4
  93. package/dist/docs/reference/hooks.html +4 -4
  94. package/dist/docs/reference/http-api.html +4 -4
  95. package/dist/docs/reference/instructions.html +4 -4
  96. package/dist/docs/reference/playground.html +4 -4
  97. package/dist/docs/reference/project-layout.html +4 -4
  98. package/dist/docs/reference/prompt.html +4 -4
  99. package/dist/docs/reference/schedules.html +4 -4
  100. package/dist/docs/reference/sessions.html +4 -4
  101. package/dist/docs/reference/skills.html +4 -4
  102. package/dist/docs/reference/subagents.html +4 -4
  103. package/dist/docs/reference/tools.html +4 -4
  104. package/dist/docs/scaffolding-agents.html +5 -5
  105. package/dist/docs/storage.html +4 -4
  106. package/dist/docs/templates/agentic-owners.html +38 -0
  107. package/dist/docs/templates/demo.html +28 -0
  108. package/dist/docs/templates/pr-autofixer.html +32 -0
  109. package/dist/docs/templates/security-reviewer.html +26 -0
  110. package/dist/docs/templates/triage.html +14 -16
  111. package/dist/docs/troubleshooting.html +4 -4
  112. package/dist/index.d.ts +3 -3
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +2 -2
  115. package/dist/internal/cli-ax.d.ts +2 -0
  116. package/dist/internal/cli-ax.d.ts.map +1 -1
  117. package/dist/internal/cli-ax.js +30 -1
  118. package/dist/internal/cli-deploy.js +1 -1
  119. package/dist/internal/cli-slack.d.ts +4 -0
  120. package/dist/internal/cli-slack.d.ts.map +1 -1
  121. package/dist/internal/cli-slack.js +46 -20
  122. package/dist/internal/cursor-event-relay.d.ts +59 -11
  123. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  124. package/dist/internal/cursor-event-relay.js +133 -26
  125. package/dist/internal/discovery.d.ts.map +1 -1
  126. package/dist/internal/discovery.js +11 -2
  127. package/dist/internal/distribution.d.ts.map +1 -1
  128. package/dist/internal/distribution.js +1 -0
  129. package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
  130. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
  131. package/dist/internal/handleAgentServeTrigger.js +97 -19
  132. package/dist/internal/init-interview.d.ts +67 -0
  133. package/dist/internal/init-interview.d.ts.map +1 -0
  134. package/dist/internal/init-interview.js +300 -0
  135. package/dist/internal/init-project.d.ts +7 -0
  136. package/dist/internal/init-project.d.ts.map +1 -1
  137. package/dist/internal/init-project.js +99 -43
  138. package/dist/internal/mcp-endpoint.d.ts +7 -0
  139. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  140. package/dist/internal/mcp-endpoint.js +7 -0
  141. package/dist/internal/server.d.ts.map +1 -1
  142. package/dist/internal/server.js +159 -57
  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/internal/slack-provision-client.js +1 -1
  151. package/dist/playground/assets/index-ClbuaLpm.css +1 -0
  152. package/dist/playground/assets/index-SqkR13k_.js +85 -0
  153. package/dist/playground/index.html +2 -2
  154. package/dist/types.d.ts +19 -12
  155. package/dist/types.d.ts.map +1 -1
  156. package/docs/README.md +15 -0
  157. package/docs/example-agents/fsd.md +3 -0
  158. package/docs/example-agents/security-reviewer.md +3 -0
  159. package/docs/guides/convert-automation.md +176 -0
  160. package/docs/guides/github.md +6 -2
  161. package/docs/reference/cli.md +36 -10
  162. package/docs/scaffolding-agents.md +3 -0
  163. package/docs/templates/agentic-owners.md +97 -0
  164. package/docs/templates/demo.md +84 -0
  165. package/docs/templates/pr-autofixer.md +136 -0
  166. package/docs/templates/security-reviewer.md +89 -0
  167. package/docs/templates/triage.md +59 -101
  168. package/package.json +9 -1
  169. package/skills/github/SKILL.md +4 -5
  170. package/src/bin/agent-serve.ts +13 -6
  171. package/src/channels/github/github-channel.ts +95 -18
  172. package/src/channels/github/types.ts +31 -0
  173. package/src/channels/origin/cursor-account.ts +64 -0
  174. package/src/channels/origin/events.ts +41 -0
  175. package/src/channels/origin/index.ts +29 -0
  176. package/src/channels/origin/origin-channel.ts +784 -0
  177. package/src/channels/origin/origin-webhook.ts +202 -0
  178. package/src/channels/origin/types.ts +186 -0
  179. package/src/channels/slack/doctor.ts +218 -5
  180. package/src/index.ts +3 -1
  181. package/src/internal/cli-ax.ts +33 -1
  182. package/src/internal/cli-deploy.ts +2 -2
  183. package/src/internal/cli-slack.ts +68 -19
  184. package/src/internal/cursor-event-relay.ts +193 -31
  185. package/src/internal/discovery.ts +15 -5
  186. package/src/internal/distribution.ts +1 -0
  187. package/src/internal/handleAgentServeTrigger.ts +129 -19
  188. package/src/internal/init-interview.ts +384 -0
  189. package/src/internal/init-project.ts +146 -46
  190. package/src/internal/mcp-endpoint.ts +7 -0
  191. package/src/internal/server.ts +188 -58
  192. package/src/internal/session-engine.ts +5 -21
  193. package/src/internal/session-links.ts +88 -0
  194. package/src/internal/sessions-client.ts +1 -0
  195. package/src/internal/slack-provision-client.ts +1 -1
  196. package/src/types.ts +19 -12
  197. package/templates/agentic-owners/README.md +73 -0
  198. package/templates/agentic-owners/agent/agent.ts +15 -0
  199. package/templates/agentic-owners/agent/channels/github.ts +85 -0
  200. package/templates/agentic-owners/agent/instructions.md +34 -0
  201. package/templates/agentic-owners/agent/lib/config.ts +13 -0
  202. package/templates/agentic-owners/agent/lib/github.ts +241 -0
  203. package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
  204. package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
  205. package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
  206. package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
  207. package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
  208. package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
  209. package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
  210. package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
  211. package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
  212. package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
  213. package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
  214. package/templates/agentic-owners/agent/lib/review.ts +777 -0
  215. package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
  216. package/templates/agentic-owners/evals/evals.config.ts +5 -0
  217. package/templates/agentic-owners/evals/owners.eval.ts +116 -0
  218. package/templates/agentic-owners/package.json +20 -0
  219. package/templates/agentic-owners/tsconfig.json +12 -0
  220. package/templates/demo/README.md +33 -0
  221. package/templates/demo/agent/agent.ts +15 -0
  222. package/templates/demo/agent/channels/github.ts +50 -0
  223. package/templates/demo/agent/channels/queue.ts +30 -0
  224. package/templates/demo/agent/instructions.md +8 -0
  225. package/templates/demo/agent/lib/collect.ts +78 -0
  226. package/templates/demo/agent/tools/record_demo.ts +40 -0
  227. package/templates/demo/evals/demo.eval.ts +33 -0
  228. package/templates/demo/evals/evals.config.ts +5 -0
  229. package/templates/demo/package.json +18 -0
  230. package/templates/demo/tsconfig.json +12 -0
  231. package/templates/pr-autofixer/README.md +44 -0
  232. package/templates/pr-autofixer/agent/agent.ts +22 -0
  233. package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
  234. package/templates/pr-autofixer/agent/channels/github.ts +167 -0
  235. package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
  236. package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
  237. package/templates/pr-autofixer/agent/instructions.md +83 -0
  238. package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
  239. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
  240. package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
  241. package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
  242. package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
  243. package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
  244. package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
  245. package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
  246. package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
  247. package/templates/pr-autofixer/agent/storage.ts +7 -0
  248. package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
  249. package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
  250. package/templates/pr-autofixer/evals/evals.config.ts +5 -0
  251. package/templates/pr-autofixer/init.json +25 -0
  252. package/templates/pr-autofixer/package.json +18 -0
  253. package/templates/pr-autofixer/tsconfig.json +12 -0
  254. package/templates/security-reviewer/README.md +31 -0
  255. package/templates/security-reviewer/agent/agent.ts +15 -0
  256. package/templates/security-reviewer/agent/channels/github.ts +46 -0
  257. package/templates/security-reviewer/agent/instructions.md +22 -0
  258. package/templates/security-reviewer/agent/lib/repos.ts +5 -0
  259. package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
  260. package/templates/security-reviewer/evals/evals.config.ts +5 -0
  261. package/templates/security-reviewer/evals/review.eval.ts +53 -0
  262. package/templates/security-reviewer/init.json +25 -0
  263. package/templates/security-reviewer/package.json +18 -0
  264. package/templates/security-reviewer/tsconfig.json +12 -0
  265. package/templates/triage/README.md +27 -29
  266. package/templates/triage/agent/agent.ts +1 -0
  267. package/templates/triage/agent/channels/queue.ts +47 -0
  268. package/templates/triage/agent/channels/webhook.ts +50 -0
  269. package/templates/triage/agent/instructions.md +24 -13
  270. package/templates/triage/agent/lib/actuate.ts +759 -0
  271. package/templates/triage/agent/lib/parse-queue-event.ts +168 -0
  272. package/templates/triage/agent/lib/tracker.ts +5 -0
  273. package/templates/triage/agent/lib/types.ts +52 -0
  274. package/templates/triage/agent/lib/verify-webhook.ts +81 -0
  275. package/templates/triage/agent/skills/triage-rubric.md +6 -5
  276. package/templates/triage/agent/tools/record_triage.ts +24 -5
  277. package/templates/triage/evals/triage.eval.ts +25 -3
  278. package/templates/triage/overlays/jira/README.md +21 -0
  279. package/templates/triage/overlays/jira/agent/lib/tracker.ts +8 -0
  280. package/templates/triage/overlays/jira/agent/mcp-connections/tracker.ts +7 -0
  281. package/templates/triage/overlays/linear/README.md +21 -0
  282. package/templates/triage/overlays/linear/agent/lib/tracker.ts +8 -0
  283. package/templates/triage/overlays/linear/agent/mcp-connections/tracker.ts +7 -0
  284. package/templates/triage/package.json +1 -1
  285. package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +0 -1
  286. package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
  287. package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
  288. package/dist/docs/assets/templates_triage.md.CConZt1z.js +0 -11
  289. package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +0 -1
  290. package/dist/playground/assets/index-BHAPd9KT.js +0 -85
  291. package/dist/playground/assets/index-DpKH0HcT.css +0 -1
  292. package/templates/triage/agent/channels/intake.ts +0 -20
  293. package/templates/triage/agent/channels/slack.ts +0 -3
@@ -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
+ }
@@ -0,0 +1,384 @@
1
+ /**
2
+ * Template `init.json`: questions `init` asks, and string replacements
3
+ * it applies while copying the scaffold.
4
+ */
5
+
6
+ import { readFile } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import { createInterface } from "node:readline";
9
+ import { parseCursorGithubRepoScope } from "../channels/github/cursor-account.js";
10
+ import { describeError } from "./describe-error.js";
11
+
12
+ export const TEMPLATE_INIT_FILE = "init.json";
13
+
14
+ export interface TemplateQuestion {
15
+ id: string;
16
+ prompt: string;
17
+ hint?: string;
18
+ placeholder?: string;
19
+ default?: string;
20
+ choices?: readonly string[];
21
+ parse?: "owner-repo-list";
22
+ }
23
+
24
+ export interface TemplateReplace {
25
+ file: string;
26
+ find: string;
27
+ answer: string;
28
+ as?: "json-array" | "first";
29
+ template?: string;
30
+ }
31
+
32
+ export interface TemplateInitSpec {
33
+ questions: TemplateQuestion[];
34
+ replace: TemplateReplace[];
35
+ }
36
+
37
+ export function posixRel(rel: string): string {
38
+ return rel.replaceAll("\\", "/");
39
+ }
40
+
41
+ export function isTemplateMetaPath(rel: string): boolean {
42
+ const normalized = posixRel(rel);
43
+ const top = normalized.split("/")[0];
44
+ if (top === "overlays") {
45
+ return true;
46
+ }
47
+ return (
48
+ normalized === TEMPLATE_INIT_FILE ||
49
+ normalized.endsWith(`/${TEMPLATE_INIT_FILE}`)
50
+ );
51
+ }
52
+
53
+ export function templateFileHasReplacements(
54
+ spec: TemplateInitSpec,
55
+ rel: string
56
+ ): boolean {
57
+ const file = posixRel(rel);
58
+ return spec.replace.some((rule) => posixRel(rule.file) === file);
59
+ }
60
+
61
+ export async function loadTemplateInitSpec(
62
+ templateDir: string
63
+ ): Promise<TemplateInitSpec | undefined> {
64
+ try {
65
+ const text = await readFile(join(templateDir, TEMPLATE_INIT_FILE), "utf8");
66
+ return parseTemplateInitSpec(JSON.parse(text));
67
+ } catch (error) {
68
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
69
+ return undefined;
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ export function parseTemplateInitSpec(value: unknown): TemplateInitSpec {
76
+ if (!isRecord(value) || !Array.isArray(value.questions)) {
77
+ throw new Error(`${TEMPLATE_INIT_FILE} must have a questions array`);
78
+ }
79
+ const questions = value.questions.map((item, index) =>
80
+ parseQuestion(item, index)
81
+ );
82
+ const replace = Array.isArray(value.replace)
83
+ ? value.replace.map((item, index) => parseReplace(item, index))
84
+ : [];
85
+ return { questions, replace };
86
+ }
87
+
88
+ export function parseInitVars(
89
+ flags: readonly string[] | undefined
90
+ ): Record<string, string> {
91
+ const vars: Record<string, string> = {};
92
+ if (flags === undefined) {
93
+ return vars;
94
+ }
95
+ for (const flag of flags) {
96
+ const eq = flag.indexOf("=");
97
+ if (eq <= 0) {
98
+ throw new Error(`--var must be id=value (got ${JSON.stringify(flag)})`);
99
+ }
100
+ vars[flag.slice(0, eq)] = flag.slice(eq + 1);
101
+ }
102
+ return vars;
103
+ }
104
+
105
+ export type ParseAnswersResult =
106
+ | { ok: true; answers: Record<string, string> }
107
+ | { ok: false; error: string };
108
+
109
+ export type RunTemplateInitResult =
110
+ | { ok: true; spec?: TemplateInitSpec; answers?: Record<string, string> }
111
+ | { ok: false; error: string };
112
+
113
+ /** Load `init.json`, apply `--var`, and prompt on a TTY. */
114
+ export async function runTemplateInit(input: {
115
+ templateDir: string;
116
+ vars?: readonly string[];
117
+ interactive: boolean;
118
+ }): Promise<RunTemplateInitResult> {
119
+ try {
120
+ const spec = await loadTemplateInitSpec(input.templateDir);
121
+ if (spec === undefined) {
122
+ return { ok: true };
123
+ }
124
+ const collected = await collectTemplateAnswers({
125
+ spec,
126
+ vars: parseInitVars(input.vars),
127
+ interactive: input.interactive,
128
+ });
129
+ if (!collected.ok) {
130
+ return collected;
131
+ }
132
+ return { ok: true, spec, answers: collected.answers };
133
+ } catch (error) {
134
+ return { ok: false, error: describeError(error) };
135
+ }
136
+ }
137
+
138
+ export function parseTemplateAnswers(
139
+ spec: TemplateInitSpec,
140
+ raw: Record<string, string>
141
+ ): ParseAnswersResult {
142
+ const answers: Record<string, string> = {};
143
+ for (const question of spec.questions) {
144
+ if (!(question.id in raw)) {
145
+ continue;
146
+ }
147
+ const parsed = parseQuestionValue(question, raw[question.id] ?? "");
148
+ if (!parsed.ok) {
149
+ return parsed;
150
+ }
151
+ answers[question.id] = parsed.value;
152
+ }
153
+ return { ok: true, answers };
154
+ }
155
+
156
+ export interface CollectAnswersInput {
157
+ spec: TemplateInitSpec;
158
+ vars?: Record<string, string>;
159
+ interactive: boolean;
160
+ ask?: (question: TemplateQuestion) => Promise<string>;
161
+ out?: (text: string) => void;
162
+ }
163
+
164
+ export async function collectTemplateAnswers(
165
+ input: CollectAnswersInput
166
+ ): Promise<ParseAnswersResult> {
167
+ const vars = input.vars ?? {};
168
+ const answers: Record<string, string> = {};
169
+ const ask = input.ask ?? promptTemplateQuestion;
170
+ const out = input.out ?? ((text) => process.stdout.write(text));
171
+
172
+ for (const question of input.spec.questions) {
173
+ if (question.id in vars) {
174
+ const parsed = parseQuestionValue(question, vars[question.id] ?? "");
175
+ if (!parsed.ok) {
176
+ return parsed;
177
+ }
178
+ answers[question.id] = parsed.value;
179
+ continue;
180
+ }
181
+ if (!input.interactive) {
182
+ continue;
183
+ }
184
+ for (;;) {
185
+ const line = await ask(question);
186
+ const value =
187
+ line.trim() === "" && question.default !== undefined
188
+ ? question.default
189
+ : line;
190
+ const parsed = parseQuestionValue(question, value);
191
+ if (parsed.ok) {
192
+ answers[question.id] = parsed.value;
193
+ break;
194
+ }
195
+ out(`${parsed.error}\n`);
196
+ }
197
+ }
198
+
199
+ return { ok: true, answers };
200
+ }
201
+
202
+ export function applyInitReplacements(
203
+ contents: string,
204
+ rel: string,
205
+ spec: TemplateInitSpec,
206
+ answers: Record<string, string>
207
+ ): string {
208
+ const file = posixRel(rel);
209
+ let next = contents;
210
+ for (const rule of spec.replace) {
211
+ if (posixRel(rule.file) !== file) {
212
+ continue;
213
+ }
214
+ const value = formatReplaceValue(rule, answers);
215
+ if (value === undefined) {
216
+ continue;
217
+ }
218
+ if (!next.includes(rule.find)) {
219
+ throw new Error(
220
+ `${TEMPLATE_INIT_FILE}: ${rule.find} not found in ${rel}`
221
+ );
222
+ }
223
+ next = replaceLiteral(next, rule.find, value);
224
+ }
225
+ return next;
226
+ }
227
+
228
+ export async function promptTemplateQuestion(
229
+ question: TemplateQuestion,
230
+ options: {
231
+ input?: NodeJS.ReadableStream;
232
+ output?: NodeJS.WritableStream;
233
+ } = {}
234
+ ): Promise<string> {
235
+ const input = options.input ?? process.stdin;
236
+ const output = options.output ?? process.stdout;
237
+ const rl = createInterface({ input, output });
238
+ const suffix = promptSuffix(question);
239
+ try {
240
+ if (question.hint !== undefined) {
241
+ output.write(` ${question.hint}\n`);
242
+ }
243
+ return await new Promise<string>((resolve) => {
244
+ rl.question(`${question.prompt}${suffix} `, resolve);
245
+ });
246
+ } finally {
247
+ rl.close();
248
+ }
249
+ }
250
+
251
+ function replaceLiteral(haystack: string, find: string, value: string): string {
252
+ return haystack.replaceAll(find, () => value);
253
+ }
254
+
255
+ function promptSuffix(question: TemplateQuestion): string {
256
+ if (question.choices !== undefined && question.choices.length > 0) {
257
+ return ` [${question.choices.join("/")}]`;
258
+ }
259
+ if (question.default !== undefined) {
260
+ return ` [${question.default}]`;
261
+ }
262
+ if (question.placeholder !== undefined) {
263
+ return ` (${question.placeholder})`;
264
+ }
265
+ return "";
266
+ }
267
+
268
+ function parseQuestionValue(
269
+ question: TemplateQuestion,
270
+ raw: string
271
+ ): { ok: true; value: string } | { ok: false; error: string } {
272
+ const trimmed = raw.trim();
273
+ if (trimmed === "") {
274
+ return { ok: false, error: `${question.id} is required.` };
275
+ }
276
+ if (question.choices !== undefined && question.choices.length > 0) {
277
+ if (!question.choices.includes(trimmed)) {
278
+ return {
279
+ ok: false,
280
+ error: `${question.id} must be one of ${question.choices.join(", ")}.`,
281
+ };
282
+ }
283
+ return { ok: true, value: trimmed };
284
+ }
285
+ if (question.parse === "owner-repo-list") {
286
+ try {
287
+ const scope = parseCursorGithubRepoScope(
288
+ trimmed.split(",").map((repo) => repo.trim())
289
+ );
290
+ return { ok: true, value: scope.repos.join(", ") };
291
+ } catch (error) {
292
+ return { ok: false, error: describeError(error) };
293
+ }
294
+ }
295
+ return { ok: true, value: trimmed };
296
+ }
297
+
298
+ function formatReplaceValue(
299
+ rule: TemplateReplace,
300
+ answers: Record<string, string>
301
+ ): string | undefined {
302
+ const raw = answers[rule.answer];
303
+ if (raw === undefined || raw === "") {
304
+ return undefined;
305
+ }
306
+ const parts = raw.split(",").map((part) => part.trim());
307
+ let value = raw;
308
+ if (rule.as === "json-array") {
309
+ value = `[${parts.map((part) => JSON.stringify(part)).join(", ")}]`;
310
+ } else if (rule.as === "first") {
311
+ const first = parts[0];
312
+ if (first === undefined || first === "") {
313
+ return undefined;
314
+ }
315
+ value = first;
316
+ }
317
+ if (rule.template !== undefined) {
318
+ return replaceLiteral(rule.template, "{{value}}", value);
319
+ }
320
+ return value;
321
+ }
322
+
323
+ function parseQuestion(value: unknown, index: number): TemplateQuestion {
324
+ if (!isRecord(value) || typeof value.id !== "string" || value.id === "") {
325
+ throw new Error(`${TEMPLATE_INIT_FILE} questions[${index}] needs an id`);
326
+ }
327
+ if (typeof value.prompt !== "string" || value.prompt === "") {
328
+ throw new Error(
329
+ `${TEMPLATE_INIT_FILE} questions[${value.id}] needs a prompt`
330
+ );
331
+ }
332
+ const question: TemplateQuestion = {
333
+ id: value.id,
334
+ prompt: value.prompt,
335
+ };
336
+ if (typeof value.hint === "string") {
337
+ question.hint = value.hint;
338
+ }
339
+ if (typeof value.placeholder === "string") {
340
+ question.placeholder = value.placeholder;
341
+ }
342
+ if (typeof value.default === "string") {
343
+ question.default = value.default;
344
+ }
345
+ if (Array.isArray(value.choices)) {
346
+ question.choices = value.choices.map((choice) => String(choice));
347
+ }
348
+ if (value.parse === "owner-repo-list") {
349
+ question.parse = "owner-repo-list";
350
+ }
351
+ return question;
352
+ }
353
+
354
+ function parseReplace(value: unknown, index: number): TemplateReplace {
355
+ if (
356
+ !isRecord(value) ||
357
+ typeof value.file !== "string" ||
358
+ value.file === "" ||
359
+ typeof value.find !== "string" ||
360
+ value.find === "" ||
361
+ typeof value.answer !== "string" ||
362
+ value.answer === ""
363
+ ) {
364
+ throw new Error(
365
+ `${TEMPLATE_INIT_FILE} replace[${index}] needs file, find, and answer`
366
+ );
367
+ }
368
+ const rule: TemplateReplace = {
369
+ file: value.file,
370
+ find: value.find,
371
+ answer: value.answer,
372
+ };
373
+ if (value.as === "json-array" || value.as === "first") {
374
+ rule.as = value.as;
375
+ }
376
+ if (typeof value.template === "string") {
377
+ rule.template = value.template;
378
+ }
379
+ return rule;
380
+ }
381
+
382
+ function isRecord(value: unknown): value is Record<string, unknown> {
383
+ return typeof value === "object" && value !== null && !Array.isArray(value);
384
+ }