@cursor/july 0.1.74 → 0.1.75

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 (126) hide show
  1. package/dist/channels/checks.d.ts +111 -0
  2. package/dist/channels/checks.d.ts.map +1 -0
  3. package/dist/channels/checks.js +86 -0
  4. package/dist/channels/deployments/deployments-channel.d.ts +44 -0
  5. package/dist/channels/deployments/deployments-channel.d.ts.map +1 -0
  6. package/dist/channels/deployments/deployments-channel.js +92 -0
  7. package/dist/channels/deployments/index.d.ts +26 -0
  8. package/dist/channels/deployments/index.d.ts.map +1 -0
  9. package/dist/channels/deployments/index.js +25 -0
  10. package/dist/channels/deployments/types.d.ts +69 -0
  11. package/dist/channels/deployments/types.d.ts.map +1 -0
  12. package/dist/channels/deployments/types.js +6 -0
  13. package/dist/channels/github/checks.d.ts +13 -48
  14. package/dist/channels/github/checks.d.ts.map +1 -1
  15. package/dist/channels/github/checks.js +56 -64
  16. package/dist/channels/origin/api.d.ts +100 -0
  17. package/dist/channels/origin/api.d.ts.map +1 -0
  18. package/dist/channels/origin/api.js +225 -0
  19. package/dist/channels/origin/binding.d.ts +44 -0
  20. package/dist/channels/origin/binding.d.ts.map +1 -0
  21. package/dist/channels/origin/binding.js +45 -0
  22. package/dist/channels/origin/checks.d.ts +107 -0
  23. package/dist/channels/origin/checks.d.ts.map +1 -0
  24. package/dist/channels/origin/checks.js +145 -0
  25. package/dist/channels/origin/index.d.ts +3 -0
  26. package/dist/channels/origin/index.d.ts.map +1 -1
  27. package/dist/channels/origin/index.js +3 -0
  28. package/dist/channels/origin/origin-channel.d.ts +4 -4
  29. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  30. package/dist/channels/origin/origin-channel.js +12 -2
  31. package/dist/channels/origin/types.d.ts +36 -0
  32. package/dist/channels/origin/types.d.ts.map +1 -1
  33. package/dist/channels.d.ts +1 -0
  34. package/dist/channels.d.ts.map +1 -1
  35. package/dist/channels.js +1 -0
  36. package/dist/docs/404.html +2 -2
  37. package/dist/docs/ab.html +3 -3
  38. package/dist/docs/assets/{app.BETd-WqU.js → app.b8bhB2SV.js} +1 -1
  39. package/dist/docs/assets/chunks/@localSearchIndexroot.DGZeXVqn.js +1 -0
  40. package/dist/docs/assets/chunks/{VPLocalSearchBox.wEaGEmV9.js → VPLocalSearchBox.lLSeIyWw.js} +1 -1
  41. package/dist/docs/assets/chunks/{theme.DNDicFOP.js → theme.C_owesa-.js} +2 -2
  42. package/dist/docs/assets/{reference_channels.md.MN8iDy8V.js → reference_channels.md.G5GysZ7e.js} +2 -2
  43. package/dist/docs/assets/{reference_channels.md.MN8iDy8V.lean.js → reference_channels.md.G5GysZ7e.lean.js} +1 -1
  44. package/dist/docs/building-with-agents.html +3 -3
  45. package/dist/docs/concepts.html +3 -3
  46. package/dist/docs/deployment.html +3 -3
  47. package/dist/docs/evals.html +3 -3
  48. package/dist/docs/example-agents/approval-buddy.html +3 -3
  49. package/dist/docs/example-agents/benny.html +3 -3
  50. package/dist/docs/example-agents/bugbot.html +3 -3
  51. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  52. package/dist/docs/example-agents/codeowners-review.html +3 -3
  53. package/dist/docs/example-agents/concierge.html +3 -3
  54. package/dist/docs/example-agents/fsd.html +3 -3
  55. package/dist/docs/example-agents/index.html +3 -3
  56. package/dist/docs/example-agents/knowledge-base.html +3 -3
  57. package/dist/docs/example-agents/oncall.html +3 -3
  58. package/dist/docs/example-agents/security-reviewer.html +3 -3
  59. package/dist/docs/example-agents/slack-agent.html +3 -3
  60. package/dist/docs/example-agents/weather-agent.html +3 -3
  61. package/dist/docs/guides/agent-to-agent.html +3 -3
  62. package/dist/docs/guides/cloud-runtime.html +3 -3
  63. package/dist/docs/guides/convert-automation.html +3 -3
  64. package/dist/docs/guides/github.html +3 -3
  65. package/dist/docs/guides/human-in-the-loop.html +3 -3
  66. package/dist/docs/guides/mcp-oauth.html +3 -3
  67. package/dist/docs/guides/opentelemetry.html +3 -3
  68. package/dist/docs/guides/slack.html +3 -3
  69. package/dist/docs/guides/webhooks.html +3 -3
  70. package/dist/docs/hashmap.json +1 -1
  71. package/dist/docs/hillclimbing.html +3 -3
  72. package/dist/docs/index.html +3 -3
  73. package/dist/docs/quickstart.html +3 -3
  74. package/dist/docs/reference/agent-config.html +3 -3
  75. package/dist/docs/reference/artifacts.html +3 -3
  76. package/dist/docs/reference/channels.html +5 -5
  77. package/dist/docs/reference/cli.html +3 -3
  78. package/dist/docs/reference/connections.html +3 -3
  79. package/dist/docs/reference/hooks.html +3 -3
  80. package/dist/docs/reference/http-api.html +3 -3
  81. package/dist/docs/reference/instructions.html +3 -3
  82. package/dist/docs/reference/playground.html +3 -3
  83. package/dist/docs/reference/project-layout.html +3 -3
  84. package/dist/docs/reference/prompt.html +3 -3
  85. package/dist/docs/reference/schedules.html +3 -3
  86. package/dist/docs/reference/sessions.html +3 -3
  87. package/dist/docs/reference/skills.html +3 -3
  88. package/dist/docs/reference/subagents.html +3 -3
  89. package/dist/docs/reference/tools.html +3 -3
  90. package/dist/docs/scaffolding-agents.html +3 -3
  91. package/dist/docs/storage.html +3 -3
  92. package/dist/docs/templates/agentic-owners.html +3 -3
  93. package/dist/docs/templates/demo.html +3 -3
  94. package/dist/docs/templates/pr-autofixer.html +3 -3
  95. package/dist/docs/templates/security-reviewer.html +3 -3
  96. package/dist/docs/templates/triage.html +3 -3
  97. package/dist/docs/troubleshooting.html +3 -3
  98. package/dist/internal/cursor-deployment-relay.d.ts +77 -0
  99. package/dist/internal/cursor-deployment-relay.d.ts.map +1 -0
  100. package/dist/internal/cursor-deployment-relay.js +201 -0
  101. package/dist/internal/distribution.d.ts.map +1 -1
  102. package/dist/internal/distribution.js +1 -0
  103. package/dist/internal/server.d.ts.map +1 -1
  104. package/dist/internal/server.js +114 -5
  105. package/dist/playground/assets/{index-D_8I9TM0.js → index-BJy9UJrF.js} +1 -1
  106. package/dist/playground/assets/index-BrSd5vff.css +1 -0
  107. package/dist/playground/index.html +2 -2
  108. package/docs/reference/channels.md +15 -0
  109. package/package.json +7 -1
  110. package/src/channels/checks.ts +185 -0
  111. package/src/channels/deployments/deployments-channel.ts +109 -0
  112. package/src/channels/deployments/index.ts +26 -0
  113. package/src/channels/deployments/types.ts +80 -0
  114. package/src/channels/github/checks.ts +90 -115
  115. package/src/channels/origin/api.ts +318 -0
  116. package/src/channels/origin/binding.ts +96 -0
  117. package/src/channels/origin/checks.ts +250 -0
  118. package/src/channels/origin/index.ts +3 -0
  119. package/src/channels/origin/origin-channel.ts +18 -6
  120. package/src/channels/origin/types.ts +37 -0
  121. package/src/channels.ts +16 -0
  122. package/src/internal/cursor-deployment-relay.ts +274 -0
  123. package/src/internal/distribution.ts +1 -0
  124. package/src/internal/server.ts +149 -0
  125. package/dist/docs/assets/chunks/@localSearchIndexroot.DM5INS71.js +0 -1
  126. package/dist/playground/assets/index-M3nSMc6_.css +0 -1
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Pull relay for `/v0/deployment-events`.
3
+ *
4
+ * A {@link RelayAdapter} over {@link CursorRelayCore} — the same engine the
5
+ * GitHub SCM and Slack relays use, so registration heartbeats, durable
6
+ * offsets, backoff, and bounded dispatch retry are inherited rather than
7
+ * reimplemented. Only the subscribe/poll paths, the registration body, and
8
+ * the per-event filter differ.
9
+ */
10
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
11
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
12
+ return new (P || (P = Promise))(function (resolve, reject) {
13
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
14
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
15
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
16
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
17
+ });
18
+ };
19
+ var __rest = (this && this.__rest) || function (s, e) {
20
+ var t = {};
21
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
22
+ t[p] = s[p];
23
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
24
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
25
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
26
+ t[p[i]] = s[p[i]];
27
+ }
28
+ return t;
29
+ };
30
+ import { CursorRelayCore } from "./cursor-relay-core.js";
31
+ const DEFAULT_EVENTS = ["deployment.succeeded"];
32
+ /** Normalize the channel's `repos` option into concrete subscriptions. */
33
+ export function resolveDeploymentSubscriptions(options) {
34
+ var _a, _b, _c, _d;
35
+ const byRepo = new Map();
36
+ for (const entry of options.repos) {
37
+ const subscription = typeof entry === "string" ? { repo: entry } : entry;
38
+ // Case-folded only: `Deployment.repository` has no documented format, and
39
+ // the server keys subscriptions the same way.
40
+ const repo = subscription.repo.trim().toLowerCase();
41
+ if (repo === "") {
42
+ throw new Error(`deploymentsChannel: invalid repo "${subscription.repo}".`);
43
+ }
44
+ const environments = [
45
+ ...new Set(((_b = (_a = subscription.environments) !== null && _a !== void 0 ? _a : options.environments) !== null && _b !== void 0 ? _b : [])
46
+ .map((value) => value.trim())
47
+ .filter(Boolean)),
48
+ ];
49
+ const events = new Set((_d = (_c = subscription.events) !== null && _c !== void 0 ? _c : options.events) !== null && _d !== void 0 ? _d : DEFAULT_EVENTS);
50
+ if (events.size === 0) {
51
+ throw new Error(`deploymentsChannel: repo "${repo}" subscribes to no events.`);
52
+ }
53
+ // Later entries for the same repo widen the earlier one rather than
54
+ // silently replacing it.
55
+ const existing = byRepo.get(repo);
56
+ if (existing === undefined) {
57
+ byRepo.set(repo, { repo, environments, events });
58
+ continue;
59
+ }
60
+ for (const event of events)
61
+ existing.events.add(event);
62
+ existing.environments =
63
+ existing.environments.length === 0 || environments.length === 0
64
+ ? []
65
+ : [...new Set([...existing.environments, ...environments])];
66
+ }
67
+ // Empty is legal here and means "this channel wants nothing", which is how
68
+ // an env-configured channel stays inert without a repo list. The wire-level
69
+ // empty list means deregister, so the host simply never registers it.
70
+ return [...byRepo.values()];
71
+ }
72
+ /** Whether one confirmed subscription wants this event. */
73
+ export function deploymentEventMatches(subscription, envelope) {
74
+ if (!subscription.events.has(envelope.eventType)) {
75
+ return false;
76
+ }
77
+ return (subscription.environments.length === 0 ||
78
+ subscription.environments.includes(envelope.environment));
79
+ }
80
+ /**
81
+ * Deliver one event to every channel subscribed to it.
82
+ *
83
+ * Each channel is delivered independently: one channel's handler throwing
84
+ * must not skip the channels after it, because the relay core retries the
85
+ * whole dispatch and the channels that already ran would see the event twice.
86
+ * Throws only when every subscribed channel failed, so a retry cannot
87
+ * re-deliver to a channel that succeeded.
88
+ */
89
+ export function fanOutDeploymentEvent(input) {
90
+ return __awaiter(this, void 0, void 0, function* () {
91
+ const { event, logger } = input;
92
+ const matched = input.targets.filter((target) => target.subscriptions.some((subscription) => subscription.repo === event.repo &&
93
+ deploymentEventMatches(subscription, event)));
94
+ const failures = [];
95
+ for (const target of matched) {
96
+ try {
97
+ yield target.deliver(event);
98
+ }
99
+ catch (error) {
100
+ failures.push(target.name);
101
+ logger(`[agent-sdk] deployment-events: ${target.name} failed on ${event.eventId}: ${error instanceof Error ? error.message : String(error)}`);
102
+ }
103
+ }
104
+ logger(`[agent-sdk] deployment-events: ${event.eventType} ${event.repo}@${event.sha} (${event.environment}) → ${matched.length - failures.length}/${matched.length} channel(s)`);
105
+ if (failures.length > 0 && failures.length === matched.length) {
106
+ throw new Error(`deployment-events: every subscribed channel failed on ${event.eventId} (${failures.join(", ")})`);
107
+ }
108
+ });
109
+ }
110
+ export class DeploymentEventRelay {
111
+ constructor(options) {
112
+ this.options = options;
113
+ this.subscribed = new Map();
114
+ this.loggedSkippedRepos = new Set();
115
+ const adapter = {
116
+ logPrefix: "deployment-events",
117
+ subscribePath: "/v0/deployment-events/subscriptions",
118
+ pollPath: "/v0/deployment-events",
119
+ buildRegistrationBody: (consumerId) => ({
120
+ consumerId,
121
+ subscriptions: options.subscriptions.map((subscription) => ({
122
+ repo: subscription.repo,
123
+ environments: subscription.environments,
124
+ events: [...subscription.events],
125
+ })),
126
+ }),
127
+ // Empty subscriptions drop this consumer's registration on clean stop
128
+ // instead of leaving it to lapse by TTL.
129
+ buildDeregistrationBody: (consumerId) => ({
130
+ consumerId,
131
+ subscriptions: [],
132
+ }),
133
+ applyRegistrationResponse: (json) => {
134
+ var _a, _b;
135
+ const result = json;
136
+ this.subscribed = new Map();
137
+ this.loggedSkippedRepos.clear();
138
+ const lines = [];
139
+ const confirmed = [];
140
+ for (const status of (_a = result.repos) !== null && _a !== void 0 ? _a : []) {
141
+ const wanted = options.subscriptions.find((subscription) => subscription.repo === status.repo);
142
+ if (!status.isSubscribed || wanted === undefined) {
143
+ lines.push(`not subscribed to ${status.repo}: ${(_b = status.detail) !== null && _b !== void 0 ? _b : "unknown reason"}`);
144
+ continue;
145
+ }
146
+ this.subscribed.set(status.repo, wanted);
147
+ confirmed.push(status.repo);
148
+ }
149
+ if (confirmed.length > 0) {
150
+ const kinds = [
151
+ ...new Set(options.subscriptions.flatMap((s) => [...s.events])),
152
+ ];
153
+ lines.push(`subscribed to ${confirmed.join(", ")} for events: ${kinds.join(", ")}`);
154
+ }
155
+ return lines;
156
+ },
157
+ shouldDispatch: (event) => this.shouldDispatch(event),
158
+ dispatch: (_a) => {
159
+ var { offset: _offset } = _a, envelope = __rest(_a, ["offset"]);
160
+ return options.dispatch(envelope);
161
+ },
162
+ };
163
+ this.core = new CursorRelayCore(Object.assign(Object.assign({ baseUrl: options.baseUrl, apiKey: options.apiKey, stateDir: options.stateDir, adapter, logger: options.logger }, (options.fetchImpl === undefined
164
+ ? {}
165
+ : { fetchImpl: options.fetchImpl })), (options.pollWaitMs === undefined
166
+ ? {}
167
+ : { pollWaitMs: options.pollWaitMs })));
168
+ }
169
+ /**
170
+ * Whether an event from this principal's stream belongs to THIS relay.
171
+ * The stream is per-principal, and one API key can back several relays
172
+ * (other hosts, or an agent running its own alongside the host's), so each
173
+ * one re-checks the events against the subscriptions the server confirmed
174
+ * for it.
175
+ */
176
+ shouldDispatch(event) {
177
+ const envelope = event;
178
+ if (envelope === null ||
179
+ typeof envelope !== "object" ||
180
+ typeof envelope.repo !== "string") {
181
+ return false;
182
+ }
183
+ const subscription = this.subscribed.get(envelope.repo);
184
+ if (subscription === undefined) {
185
+ if (!this.loggedSkippedRepos.has(envelope.repo)) {
186
+ this.loggedSkippedRepos.add(envelope.repo);
187
+ this.options.logger(`[agent-sdk] deployment-events: skipping events for ${envelope.repo}: not in this host's subscription (another consumer of this API key likely subscribed it)`);
188
+ }
189
+ return false;
190
+ }
191
+ return deploymentEventMatches(subscription, envelope);
192
+ }
193
+ start() {
194
+ this.core.start();
195
+ }
196
+ stop() {
197
+ return __awaiter(this, void 0, void 0, function* () {
198
+ yield this.core.stop();
199
+ });
200
+ }
201
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"distribution.d.ts","sourceRoot":"","sources":["../../src/internal/distribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,sEAAsE;AACtE,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,2BAA2B,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,UAAU,EAAE,MAA2B,CAAC;AACrD,eAAO,MAAM,gBAAgB,EAAE,MAAiC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAA+B,CAAC;AAc/D;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,kEAAkE;AAClE,wBAAgB,cAAc,IAAI,MAAM,CAOvC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAC/C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CA2BzC,CAAC"}
1
+ {"version":3,"file":"distribution.d.ts","sourceRoot":"","sources":["../../src/internal/distribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,sEAAsE;AACtE,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,2BAA2B,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,UAAU,EAAE,MAA2B,CAAC;AACrD,eAAO,MAAM,gBAAgB,EAAE,MAAiC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAA+B,CAAC;AAc/D;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,kEAAkE;AAClE,wBAAgB,cAAc,IAAI,MAAM,CAOvC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAC/C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CA4BzC,CAAC"}
@@ -81,6 +81,7 @@ export const AUTHORING_ENTRY_FILES = [
81
81
  ["channels/slack", "channels/slack/index.js"],
82
82
  ["channels/github", "channels/github/index.js"],
83
83
  ["channels/origin", "channels/origin/index.js"],
84
+ ["channels/deployments", "channels/deployments/index.js"],
84
85
  ["skills", "skills.js"],
85
86
  ["schedules", "schedules.js"],
86
87
  ["reminders", "reminders.js"],
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkCH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAOhB,YAAY,EACb,MAAM,aAAa,CAAC;AAkHrB,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,iBAAiB,CAAC;AAwBpE,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;CACvB;AAaD,6EAA6E;AAC7E,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,GAAE,YAAY,GAAG,0BAA+B,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAywB3B"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuCH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAOhB,YAAY,EACb,MAAM,aAAa,CAAC;AAwHrB,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,iBAAiB,CAAC;AAwBpE,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;CACvB;AAaD,6EAA6E;AAC7E,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,GAAE,YAAY,GAAG,0BAA+B,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAoxB3B"}
@@ -35,6 +35,7 @@ import { Buffer } from "node:buffer";
35
35
  import { once } from "node:events";
36
36
  import { createServer, } from "node:http";
37
37
  import { join } from "node:path";
38
+ import { dispatchDeploymentEvent, getDeploymentsChannelOptions, } from "../channels/deployments/deployments-channel.js";
38
39
  import { createCursorAccountGithubTokenResolver, getGithubCursorAccountRuntime, parseCursorGithubRepoScope, resolveCursorGithubPermissions, resolveCursorGithubProxyUrl, } from "../channels/github/cursor-account.js";
39
40
  import { findGitHubChannelTargets } from "../channels/github/forward.js";
40
41
  import { getOriginCursorAccountRuntime, parseCursorOriginRepoScope, } from "../channels/origin/cursor-account.js";
@@ -46,6 +47,7 @@ import { CursorAccountMcpBridge, collectCursorAccountConnections, cursorAccountM
46
47
  import { CursorBackendClient } from "./cursor/backend-client.js";
47
48
  import { cursorExternalApiUrl, resolveApiKey, SIGN_IN_HINT, } from "./cursor/credentials.js";
48
49
  import { CursorGithubCredentialProvider } from "./cursor/github-credentials.js";
50
+ import { DeploymentEventRelay, fanOutDeploymentEvent, resolveDeploymentSubscriptions, } from "./cursor-deployment-relay.js";
49
51
  import { CursorEventRelay, formatCursorScmDispatchOutcome, formatCursorScmEventLog, scmEventToGitHubPayload, scmMetadataToOriginPayload, } from "./cursor-event-relay.js";
50
52
  import { AGENT_SERVE_GENERATION_ENV_VAR } from "./cursor-relay-core.js";
51
53
  import { CursorSlackRelay, createCursorSlackApiCaller, formatCursorSlackEventLog, } from "./cursor-slack-relay.js";
@@ -536,6 +538,14 @@ export function startServer(mounts_1) {
536
538
  cursorAccountOnly: options.cursorEvents === undefined &&
537
539
  (cursorGithubTargets.length > 0 || cursorOriginTargets.length > 0),
538
540
  });
541
+ // Cursor deployment-events relay: automatic when a mounted agent authored
542
+ // `deploymentsChannel({ ... })`.
543
+ const cursorDeploymentRelay = startCursorDeploymentRelay({
544
+ options,
545
+ runtimes,
546
+ accountKey,
547
+ logger,
548
+ });
539
549
  const server = createServer((req, res) => {
540
550
  void instrumentHttp(otelRuntime, req, res, () => handleRequest(router, req, res, logger));
541
551
  });
@@ -553,6 +563,7 @@ export function startServer(mounts_1) {
553
563
  });
554
564
  }
555
565
  catch (error) {
566
+ yield cursorDeploymentRelay === null || cursorDeploymentRelay === void 0 ? void 0 : cursorDeploymentRelay.stop();
556
567
  yield cursorEventRelay === null || cursorEventRelay === void 0 ? void 0 : cursorEventRelay.stop();
557
568
  yield cursorSlackRelay === null || cursorSlackRelay === void 0 ? void 0 : cursorSlackRelay.stop();
558
569
  cursorGithubCredentials === null || cursorGithubCredentials === void 0 ? void 0 : cursorGithubCredentials.stop();
@@ -703,6 +714,7 @@ export function startServer(mounts_1) {
703
714
  var _a;
704
715
  var _b;
705
716
  playgroundDev === null || playgroundDev === void 0 ? void 0 : playgroundDev.stop();
717
+ yield cursorDeploymentRelay === null || cursorDeploymentRelay === void 0 ? void 0 : cursorDeploymentRelay.stop();
706
718
  yield cursorEventRelay === null || cursorEventRelay === void 0 ? void 0 : cursorEventRelay.stop();
707
719
  yield cursorSlackRelay === null || cursorSlackRelay === void 0 ? void 0 : cursorSlackRelay.stop();
708
720
  for (const runtime of runtimes) {
@@ -1660,13 +1672,110 @@ function collectCursorSlackTargets(runtimes) {
1660
1672
  }
1661
1673
  return targets;
1662
1674
  }
1675
+ /** Every mounted `deploymentsChannel`, with its resolved subscriptions. */
1676
+ function collectCursorDeploymentTargets(runtimes, skipped) {
1677
+ const targets = [];
1678
+ for (const runtime of runtimes) {
1679
+ for (const channel of runtime.project.channels) {
1680
+ const options = getDeploymentsChannelOptions(channel.definition);
1681
+ if (options === undefined) {
1682
+ continue;
1683
+ }
1684
+ const subscriptions = resolveDeploymentSubscriptions(options);
1685
+ if (subscriptions.length === 0) {
1686
+ // Declared the channel but named no repositories — usually an unset
1687
+ // env var. Say so rather than starting a relay that receives nothing.
1688
+ skipped.push(`${runtime.slug || runtime.project.name}/${channel.id}`);
1689
+ continue;
1690
+ }
1691
+ targets.push({ runtime, channelId: channel.id, options, subscriptions });
1692
+ }
1693
+ }
1694
+ return targets;
1695
+ }
1663
1696
  /**
1664
- * Automatic Cursor Slack relay for cursor-account Slack channels: registers
1665
- * the mounted agents with `/v0/slack-events`, binds each channel's outbound
1666
- * API transport to the backend proxy, and dispatches relayed envelopes into
1667
- * the pack's normal inbound path. Requires a signed-in hostfail serve
1668
- * fast rather than mounting channels that can never hear or speak.
1697
+ * Cursor deployment-events relay: automatic when a mounted agent authored
1698
+ * `deploymentsChannel({ ... })`. One relay per process reads
1699
+ * `/v0/deployment-events` for the union of every channel's repositories, and
1700
+ * each event is routed to the channels that asked for it the same ownership
1701
+ * `startCursorSlackRelay` and `startCursorEventRelay` have.
1669
1702
  */
1703
+ function startCursorDeploymentRelay(input) {
1704
+ var _a;
1705
+ const { options, runtimes, accountKey, logger } = input;
1706
+ const skipped = [];
1707
+ const targets = collectCursorDeploymentTargets(runtimes, skipped);
1708
+ if (skipped.length > 0) {
1709
+ logger(`[agent-sdk] deployment-events: no repositories declared on ${skipped.join(", ")}; those channels receive nothing`);
1710
+ }
1711
+ if (targets.length === 0) {
1712
+ return undefined;
1713
+ }
1714
+ if (accountKey === undefined) {
1715
+ throw new Error(`This project has channels subscribed to deployment events (deploymentsChannel({ repos })) but the host is not signed in to Cursor. ${SIGN_IN_HINT}`);
1716
+ }
1717
+ // Union across channels: one registration per repo for the whole process,
1718
+ // however many channels named it. Per-channel environment and event-type
1719
+ // filtering happens at dispatch.
1720
+ const byRepo = new Map();
1721
+ for (const target of targets) {
1722
+ for (const subscription of target.subscriptions) {
1723
+ const existing = byRepo.get(subscription.repo);
1724
+ if (existing === undefined) {
1725
+ byRepo.set(subscription.repo, {
1726
+ repo: subscription.repo,
1727
+ environments: [...subscription.environments],
1728
+ events: new Set(subscription.events),
1729
+ });
1730
+ continue;
1731
+ }
1732
+ for (const event of subscription.events)
1733
+ existing.events.add(event);
1734
+ // An empty environment list means "every environment", so it absorbs.
1735
+ existing.environments =
1736
+ existing.environments.length === 0 ||
1737
+ subscription.environments.length === 0
1738
+ ? []
1739
+ : [
1740
+ ...new Set([
1741
+ ...existing.environments,
1742
+ ...subscription.environments,
1743
+ ]),
1744
+ ];
1745
+ }
1746
+ }
1747
+ // Same host split as scm-events and slack-events: `/v0/deployment-events/*`
1748
+ // is on api.cursor.com, not api2.
1749
+ const baseStateRoot = (_a = options.stateRoot) !== null && _a !== void 0 ? _a : join(targets[0].runtime.project.rootDir, ".agent-serve");
1750
+ const relay = new DeploymentEventRelay({
1751
+ baseUrl: cursorExternalApiUrl(),
1752
+ apiKey: accountKey.apiKey,
1753
+ stateDir: join(baseStateRoot, "deployment-events"),
1754
+ subscriptions: [...byRepo.values()],
1755
+ logger,
1756
+ dispatch: (event) => __awaiter(this, void 0, void 0, function* () {
1757
+ return fanOutDeploymentEvent({
1758
+ event,
1759
+ logger,
1760
+ targets: targets.map((target) => ({
1761
+ name: `${target.runtime.slug || target.runtime.project.name}/${target.channelId}`,
1762
+ subscriptions: target.subscriptions,
1763
+ deliver: (delivered) => dispatchDeploymentEvent({
1764
+ options: target.options,
1765
+ event: delivered,
1766
+ args: buildChannelHandlerArgs({
1767
+ runtime: target.runtime,
1768
+ channelId: target.channelId,
1769
+ }),
1770
+ }),
1771
+ })),
1772
+ });
1773
+ }),
1774
+ });
1775
+ relay.start();
1776
+ logger(`[agent-sdk] deployment-events: relaying ${byRepo.size} repository subscription(s) to ${targets.length} channel(s)`);
1777
+ return relay;
1778
+ }
1670
1779
  function startCursorSlackRelay(input) {
1671
1780
  return __awaiter(this, void 0, void 0, function* () {
1672
1781
  var _a;