@cursor/july 0.1.32 → 0.1.34

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 (186) hide show
  1. package/README.md +1 -1
  2. package/dist/artifacts.d.ts.map +1 -1
  3. package/dist/artifacts.js +0 -5
  4. package/dist/bin/agent-serve.d.ts +1 -0
  5. package/dist/bin/agent-serve.d.ts.map +1 -1
  6. package/dist/bin/agent-serve.js +32 -2
  7. package/dist/channels/github/types.d.ts +10 -7
  8. package/dist/channels/github/types.d.ts.map +1 -1
  9. package/dist/docs/404.html +2 -2
  10. package/dist/docs/ab.html +3 -3
  11. package/dist/docs/assets/{app.C2aVczmo.js → app.FPupl4SP.js} +1 -1
  12. package/dist/docs/assets/chunks/@localSearchIndexroot.WoYunhnT.js +1 -0
  13. package/dist/docs/assets/chunks/{VPLocalSearchBox.DLEzetbD.js → VPLocalSearchBox.Cd182Cu0.js} +1 -1
  14. package/dist/docs/assets/chunks/{theme.BkOH1oMp.js → theme.BEM3Okcd.js} +2 -2
  15. package/dist/docs/assets/{example-agents_weather-agent.md.Dmrcphhl.js → example-agents_weather-agent.md.BADkPqxQ.js} +6 -4
  16. package/dist/docs/assets/example-agents_weather-agent.md.BADkPqxQ.lean.js +1 -0
  17. package/dist/docs/assets/guides_cloud-runtime.md.CDJGvVC4.js +9 -0
  18. package/dist/docs/assets/guides_cloud-runtime.md.CDJGvVC4.lean.js +1 -0
  19. package/dist/docs/building-with-agents.html +3 -3
  20. package/dist/docs/concepts.html +3 -3
  21. package/dist/docs/deployment.html +3 -3
  22. package/dist/docs/evals.html +3 -3
  23. package/dist/docs/example-agents/approval-buddy.html +3 -3
  24. package/dist/docs/example-agents/benny.html +3 -3
  25. package/dist/docs/example-agents/bugbot.html +3 -3
  26. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  27. package/dist/docs/example-agents/codeowners-review.html +3 -3
  28. package/dist/docs/example-agents/concierge.html +3 -3
  29. package/dist/docs/example-agents/fsd.html +3 -3
  30. package/dist/docs/example-agents/index.html +3 -3
  31. package/dist/docs/example-agents/knowledge-base.html +3 -3
  32. package/dist/docs/example-agents/oncall.html +3 -3
  33. package/dist/docs/example-agents/security-reviewer.html +3 -3
  34. package/dist/docs/example-agents/slack-agent.html +3 -3
  35. package/dist/docs/example-agents/weather-agent.html +10 -8
  36. package/dist/docs/guides/agent-to-agent.html +3 -3
  37. package/dist/docs/guides/cloud-runtime.html +5 -5
  38. package/dist/docs/guides/github.html +3 -3
  39. package/dist/docs/guides/human-in-the-loop.html +3 -3
  40. package/dist/docs/guides/mcp-oauth.html +3 -3
  41. package/dist/docs/guides/slack.html +3 -3
  42. package/dist/docs/guides/webhooks.html +3 -3
  43. package/dist/docs/hashmap.json +1 -1
  44. package/dist/docs/hillclimbing.html +3 -3
  45. package/dist/docs/index.html +3 -3
  46. package/dist/docs/quickstart.html +3 -3
  47. package/dist/docs/reference/agent-config.html +3 -3
  48. package/dist/docs/reference/channels.html +3 -3
  49. package/dist/docs/reference/cli.html +3 -3
  50. package/dist/docs/reference/connections.html +3 -3
  51. package/dist/docs/reference/hooks.html +3 -3
  52. package/dist/docs/reference/http-api.html +3 -3
  53. package/dist/docs/reference/instructions.html +3 -3
  54. package/dist/docs/reference/playground.html +3 -3
  55. package/dist/docs/reference/project-layout.html +3 -3
  56. package/dist/docs/reference/prompt.html +3 -3
  57. package/dist/docs/reference/schedules.html +3 -3
  58. package/dist/docs/reference/sessions.html +3 -3
  59. package/dist/docs/reference/skills.html +3 -3
  60. package/dist/docs/reference/subagents.html +3 -3
  61. package/dist/docs/reference/tools.html +3 -3
  62. package/dist/docs/scaffolding-agents.html +3 -3
  63. package/dist/docs/storage.html +3 -3
  64. package/dist/docs/troubleshooting.html +3 -3
  65. package/dist/files-backends/agent-store-presigned-url.d.ts.map +1 -1
  66. package/dist/files-backends/agent-store-presigned-url.js +19 -810
  67. package/dist/files.d.ts +20 -0
  68. package/dist/files.d.ts.map +1 -1
  69. package/dist/files.js +20 -0
  70. package/dist/index.d.ts +2 -2
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +2 -2
  73. package/dist/internal/artifacts-store.d.ts +14 -6
  74. package/dist/internal/artifacts-store.d.ts.map +1 -1
  75. package/dist/internal/artifacts-store.js +160 -16
  76. package/dist/internal/cli-convert-automation.d.ts +39 -0
  77. package/dist/internal/cli-convert-automation.d.ts.map +1 -0
  78. package/dist/internal/cli-convert-automation.js +280 -0
  79. package/dist/internal/cli-deploy.d.ts +2 -0
  80. package/dist/internal/cli-deploy.d.ts.map +1 -1
  81. package/dist/internal/cli-deploy.js +1 -1
  82. package/dist/internal/cli-mcp-oauth.d.ts +23 -0
  83. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  84. package/dist/internal/cli-mcp-oauth.js +57 -33
  85. package/dist/internal/convert-automation/convert-workflow.d.ts +10 -0
  86. package/dist/internal/convert-automation/convert-workflow.d.ts.map +1 -0
  87. package/dist/internal/convert-automation/convert-workflow.js +998 -0
  88. package/dist/internal/convert-automation/index.d.ts +3 -0
  89. package/dist/internal/convert-automation/index.d.ts.map +1 -0
  90. package/dist/internal/convert-automation/index.js +1 -0
  91. package/dist/internal/convert-automation/slug.d.ts +11 -0
  92. package/dist/internal/convert-automation/slug.d.ts.map +1 -0
  93. package/dist/internal/convert-automation/slug.js +36 -0
  94. package/dist/internal/convert-automation/types.d.ts +191 -0
  95. package/dist/internal/convert-automation/types.d.ts.map +1 -0
  96. package/dist/internal/convert-automation/types.js +13 -0
  97. package/dist/internal/cursor-relay-core.d.ts +12 -0
  98. package/dist/internal/cursor-relay-core.d.ts.map +1 -1
  99. package/dist/internal/cursor-relay-core.js +42 -8
  100. package/dist/internal/deploy-client.d.ts +31 -0
  101. package/dist/internal/deploy-client.d.ts.map +1 -1
  102. package/dist/internal/deploy-client.js +48 -0
  103. package/dist/internal/discovery.d.ts.map +1 -1
  104. package/dist/internal/discovery.js +3 -32
  105. package/dist/internal/init-project.d.ts +1 -0
  106. package/dist/internal/init-project.d.ts.map +1 -1
  107. package/dist/internal/init-project.js +1 -1
  108. package/dist/internal/mcp-endpoint.d.ts +6 -0
  109. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  110. package/dist/internal/mcp-endpoint.js +168 -0
  111. package/dist/internal/resolved-connections.d.ts +15 -0
  112. package/dist/internal/resolved-connections.d.ts.map +1 -1
  113. package/dist/internal/resolved-connections.js +12 -5
  114. package/dist/internal/review-comments.d.ts +9 -1
  115. package/dist/internal/review-comments.d.ts.map +1 -1
  116. package/dist/internal/review-comments.js +76 -0
  117. package/dist/internal/server.d.ts.map +1 -1
  118. package/dist/internal/server.js +83 -8
  119. package/dist/internal/session-engine.d.ts +56 -3
  120. package/dist/internal/session-engine.d.ts.map +1 -1
  121. package/dist/internal/session-engine.js +252 -83
  122. package/dist/internal/storage-coordinator.d.ts +0 -15
  123. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  124. package/dist/internal/storage-coordinator.js +0 -52
  125. package/dist/internal/workspace.d.ts +10 -5
  126. package/dist/internal/workspace.d.ts.map +1 -1
  127. package/dist/internal/workspace.js +13 -0
  128. package/dist/memory.d.ts +22 -6
  129. package/dist/memory.d.ts.map +1 -1
  130. package/dist/memory.js +100 -8
  131. package/dist/playground/assets/index-CDDWw0YX.js +85 -0
  132. package/dist/playground/assets/index-MVuNTd8v.css +1 -0
  133. package/dist/playground/index.html +2 -2
  134. package/dist/storage.d.ts +0 -4
  135. package/dist/storage.d.ts.map +1 -1
  136. package/dist/storage.js +0 -4
  137. package/dist/types.d.ts +18 -0
  138. package/dist/types.d.ts.map +1 -1
  139. package/docs/example-agents/weather-agent.md +29 -11
  140. package/docs/guides/cloud-runtime.md +6 -4
  141. package/package.json +3 -22
  142. package/src/artifacts.ts +0 -6
  143. package/src/bin/agent-serve.ts +36 -2
  144. package/src/channels/github/types.ts +10 -7
  145. package/src/files-backends/agent-store-presigned-url.ts +7 -5
  146. package/src/files.ts +27 -0
  147. package/src/index.ts +6 -1
  148. package/src/internal/artifacts-store.ts +185 -22
  149. package/src/internal/cli-convert-automation.ts +352 -0
  150. package/src/internal/cli-deploy.ts +1 -1
  151. package/src/internal/cli-mcp-oauth.ts +69 -31
  152. package/src/internal/convert-automation/convert-workflow.ts +1422 -0
  153. package/src/internal/convert-automation/index.ts +10 -0
  154. package/src/internal/convert-automation/slug.ts +38 -0
  155. package/src/internal/convert-automation/types.ts +199 -0
  156. package/src/internal/cursor-relay-core.ts +62 -3
  157. package/src/internal/deploy-client.ts +78 -0
  158. package/src/internal/discovery.ts +3 -37
  159. package/src/internal/init-project.ts +1 -1
  160. package/src/internal/mcp-endpoint.ts +243 -0
  161. package/src/internal/resolved-connections.ts +32 -6
  162. package/src/internal/review-comments.ts +102 -2
  163. package/src/internal/server.ts +108 -2
  164. package/src/internal/session-engine.ts +329 -99
  165. package/src/internal/storage-coordinator.ts +0 -62
  166. package/src/internal/workspace.ts +19 -5
  167. package/src/memory.ts +126 -10
  168. package/src/storage.ts +0 -6
  169. package/src/types.ts +15 -0
  170. package/dist/channels/slack/post-update-delivery.d.ts +0 -85
  171. package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
  172. package/dist/docs/assets/chunks/@localSearchIndexroot.xgMnlUSO.js +0 -1
  173. package/dist/docs/assets/example-agents_weather-agent.md.Dmrcphhl.lean.js +0 -1
  174. package/dist/docs/assets/guides_cloud-runtime.md.BZ2GA7Es.js +0 -9
  175. package/dist/docs/assets/guides_cloud-runtime.md.BZ2GA7Es.lean.js +0 -1
  176. package/dist/internal/json-dir-store.d.ts +0 -32
  177. package/dist/internal/json-dir-store.d.ts.map +0 -1
  178. package/dist/internal/persistence-coordinator.d.ts +0 -127
  179. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  180. package/dist/multi-tenant.d.ts +0 -80
  181. package/dist/multi-tenant.d.ts.map +0 -1
  182. package/dist/multi-tenant.js +0 -69
  183. package/dist/persistence.d.ts +0 -184
  184. package/dist/persistence.d.ts.map +0 -1
  185. package/dist/playground/assets/index-50PKeJlG.css +0 -1
  186. package/dist/playground/assets/index-C0f2Wl1q.js +0 -85
@@ -0,0 +1,10 @@
1
+ export { convertWorkflow } from "./convert-workflow.js";
2
+ export type {
3
+ ConvertAutomationMetadata,
4
+ ConvertedFile,
5
+ ConvertedMcpConnection,
6
+ ConvertMcpServer,
7
+ ConvertWorkflowInput,
8
+ ConvertWorkflowResult,
9
+ WorkflowJson,
10
+ } from "./types.js";
@@ -0,0 +1,38 @@
1
+ /** Filename slugs for generated project files. */
2
+
3
+ /** Lowercase, dashes, alphanumerics only; `fallback` when nothing survives. */
4
+ export function slugify(name: string, fallback: string): string {
5
+ const slug = name
6
+ .toLowerCase()
7
+ .replace(/[^a-z0-9]+/g, "-")
8
+ .replace(/^-+|-+$/g, "");
9
+ return slug === "" ? fallback : slug;
10
+ }
11
+
12
+ /** Single-token PascalCase (Slack cursorAccount agentName). */
13
+ export function pascalCase(name: string, fallback: string): string {
14
+ const words = name.split(/[^a-zA-Z0-9]+/).filter((word) => word !== "");
15
+ if (words.length === 0) {
16
+ return fallback;
17
+ }
18
+ return words.map((word) => word[0]?.toUpperCase() + word.slice(1)).join("");
19
+ }
20
+
21
+ /** Dedupes slugs with numeric suffixes: `linear`, `linear-2`, `linear-3`, … */
22
+ export class SlugRegistry {
23
+ private readonly used = new Set<string>();
24
+
25
+ claim(base: string): string {
26
+ if (!this.used.has(base)) {
27
+ this.used.add(base);
28
+ return base;
29
+ }
30
+ for (let suffix = 2; ; suffix++) {
31
+ const candidate = `${base}-${suffix}`;
32
+ if (!this.used.has(candidate)) {
33
+ this.used.add(candidate);
34
+ return candidate;
35
+ }
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Types for `convertWorkflow` — the pure automation → agentkit project
3
+ * converter behind `agent-serve convert-automation`.
4
+ *
5
+ * The workflow arrives as generic proto3 JSON (lowerCamelCase field names,
6
+ * enum value names as strings) produced by the backend export endpoint
7
+ * (`Workflow.toJson()` over `schema/aiserver/v1/automations.proto`).
8
+ * `@anysphere/proto` is not a dependency of this package, so instead of
9
+ * `Workflow.fromJson` we model the subset of the JSON shape the converter
10
+ * reads as local structural types. Unknown fields are ignored by
11
+ * construction; unknown trigger/action variants surface as warnings.
12
+ */
13
+
14
+ /** Automation metadata from the export endpoint (`AutomationExportResponse`). */
15
+ export interface ConvertAutomationMetadata {
16
+ uuid: string;
17
+ name: string;
18
+ enabled: boolean;
19
+ /** DB scope enum name, e.g. "USER" | "TEAM" | "TEAM_EDITABLE_USER". */
20
+ scope: string;
21
+ webUrl: string;
22
+ }
23
+
24
+ /**
25
+ * MCP server entry from the export endpoint (`AutomationExportMcpServer`):
26
+ * name + transport references only. Non-stdio servers convert to
27
+ * Cursor-account connections resolved at runtime.
28
+ */
29
+ export interface ConvertMcpServer {
30
+ id?: string;
31
+ name: string;
32
+ transportType: "http" | "sse" | "stdio";
33
+ }
34
+
35
+ export interface ConvertWorkflowInput {
36
+ automation: ConvertAutomationMetadata;
37
+ /** The Workflow as generic proto3 JSON. */
38
+ workflow: WorkflowJson;
39
+ /** MCP servers resolved by the export endpoint (not from workflow refs). */
40
+ mcpServers: ConvertMcpServer[];
41
+ /**
42
+ * Export timestamp supplied by the caller (ISO 8601). Passed in so the
43
+ * conversion stays fully deterministic.
44
+ */
45
+ exportedAt: string;
46
+ }
47
+
48
+ export interface ConvertedFile {
49
+ path: string;
50
+ contents: string;
51
+ }
52
+
53
+ export interface ConvertedMcpConnection {
54
+ /** Connection (file) name under `agent/mcp-connections/`. */
55
+ name: string;
56
+ /** Runtime-resolved Cursor-account connection, or a .todo.md stdio stub. */
57
+ kind: "cursorAccount" | "stdioStub";
58
+ }
59
+
60
+ export interface ConvertWorkflowResult {
61
+ files: ConvertedFile[];
62
+ warnings: string[];
63
+ setupSteps: string[];
64
+ mcpConnections: ConvertedMcpConnection[];
65
+ }
66
+
67
+ // ============================================================================
68
+ // Workflow proto3 JSON shapes (subset the converter reads)
69
+ // ============================================================================
70
+
71
+ export interface WorkflowJson {
72
+ managedConfig?: Record<string, unknown>;
73
+ triggers?: TriggerJson[];
74
+ actions?: ActionJson[];
75
+ prompts?: PromptJson[];
76
+ model?: string;
77
+ gitConfig?: GitConfigJson;
78
+ agentOptions?: AgentOptionsJson;
79
+ memoryEnabled?: boolean;
80
+ disabledDefaultTools?: string[];
81
+ }
82
+
83
+ export interface GitConfigJson {
84
+ repo?: string;
85
+ branch?: string;
86
+ repos?: string[];
87
+ }
88
+
89
+ export interface AgentOptionsJson {
90
+ skipInstall?: boolean;
91
+ privateWorker?: { labels?: Array<{ key?: string; value?: string }> };
92
+ environmentPublicId?: string;
93
+ }
94
+
95
+ export interface PromptJson {
96
+ prompt?: string;
97
+ /** Enum name (e.g. "PROMPT_EFFORT_LEVEL_HARD") or number. */
98
+ effortLevel?: string | number;
99
+ model?: string;
100
+ suppressSlackReaction?: boolean;
101
+ }
102
+
103
+ /**
104
+ * A workflow trigger. Exactly one of the variant fields is set (proto oneof).
105
+ */
106
+ export interface TriggerJson {
107
+ cron?: CronTriggerJson;
108
+ git?: GitTriggerJson;
109
+ slackTrigger?: SlackWatchTriggerJson;
110
+ linear?: Record<string, unknown>;
111
+ webhook?: Record<string, unknown>;
112
+ slackChannelCreated?: Record<string, unknown>;
113
+ pagerduty?: Record<string, unknown>;
114
+ sentry?: Record<string, unknown>;
115
+ microsoftTeamsTrigger?: Record<string, unknown>;
116
+ microsoftTeamsChannelCreated?: Record<string, unknown>;
117
+ slackReactionAdded?: SlackChannelsTriggerJson;
118
+ slackMention?: SlackChannelsTriggerJson;
119
+ slackAnyReactionAdded?: SlackChannelsTriggerJson;
120
+ }
121
+
122
+ export interface CronTriggerJson {
123
+ cron?: string;
124
+ }
125
+
126
+ /**
127
+ * Git trigger. Exactly one event variant is set; each carries `repos` and
128
+ * event-specific filter fields the converter only echoes into comments.
129
+ */
130
+ export interface GitTriggerJson {
131
+ pullRequest?: GitEventJson;
132
+ push?: GitEventJson;
133
+ ciCompleted?: GitEventJson;
134
+ issueLabeled?: GitEventJson;
135
+ label?: GitEventJson & { pullRequests?: boolean; issues?: boolean };
136
+ issueComment?: GitEventJson;
137
+ pullRequestReviewComment?: GitEventJson;
138
+ pullRequestReview?: GitEventJson;
139
+ reviewThread?: GitEventJson;
140
+ workflowRun?: GitEventJson;
141
+ pullRequestReviewRequested?: GitEventJson;
142
+ issueAssigned?: GitEventJson;
143
+ userAllowlist?: string[];
144
+ }
145
+
146
+ export interface GitEventJson {
147
+ repo?: string;
148
+ repos?: string[];
149
+ orgs?: string[];
150
+ /** Only on push events: branch filter enforced by Cursor's backend. */
151
+ branch?: string;
152
+ /** Enum name, only on pullRequest events (e.g. "GIT_PULL_REQUEST_ACTION_COMMENTED"). */
153
+ prAction?: string | number;
154
+ [key: string]: unknown;
155
+ }
156
+
157
+ /** SlackTrigger (channel watch). */
158
+ export interface SlackWatchTriggerJson {
159
+ channel?: string;
160
+ channels?: string[];
161
+ messageContains?: string;
162
+ messageContainsIsRegex?: boolean;
163
+ topLevelOnly?: boolean;
164
+ [key: string]: unknown;
165
+ }
166
+
167
+ /** Shared shape of SlackMention / SlackReactionAdded / SlackAnyReactionAdded. */
168
+ export interface SlackChannelsTriggerJson {
169
+ channel?: string;
170
+ channels?: string[];
171
+ [key: string]: unknown;
172
+ }
173
+
174
+ /**
175
+ * A workflow action. Exactly one variant field is set (proto oneof). Only
176
+ * `mcp` carries data the converter reads; the rest map to
177
+ * "behaviors to verify" setup steps.
178
+ */
179
+ export interface ActionJson {
180
+ gitPr?: Record<string, unknown>;
181
+ prComment?: Record<string, unknown>;
182
+ slack?: SlackActionJson;
183
+ mcp?: { server?: { id?: string; name?: string } };
184
+ manageCheckRun?: Record<string, unknown>;
185
+ requestReviewers?: Record<string, unknown>;
186
+ readSlack?: Record<string, unknown>;
187
+ approvePr?: Record<string, unknown>;
188
+ resolveReviewThreads?: Record<string, unknown>;
189
+ microsoftTeams?: Record<string, unknown>;
190
+ readMicrosoftTeams?: Record<string, unknown>;
191
+ }
192
+
193
+ export interface SlackActionJson {
194
+ channel?: string;
195
+ channels?: string[];
196
+ generalized?: boolean;
197
+ postAsThread?: boolean;
198
+ [key: string]: unknown;
199
+ }
@@ -151,6 +151,42 @@ export interface CursorRelayCoreOptions<TEvent extends { offset: string }> {
151
151
  fetchImpl?: typeof fetch;
152
152
  /** Test seam: override long-poll wait (default 25s). */
153
153
  pollWaitMs?: number;
154
+ /**
155
+ * Desired-revision generation for hosted engines. Sent as
156
+ * `x-agent-serve-generation` so the control plane can fence zombies after
157
+ * a redeploy. Defaults to `process.env.AGENT_SERVE_GENERATION` when set.
158
+ */
159
+ deploymentGeneration?: number;
160
+ }
161
+
162
+ /** Must match server-shared `AGENT_SERVE_GENERATION_HEADER`. */
163
+ export const AGENT_SERVE_GENERATION_HEADER = "x-agent-serve-generation";
164
+
165
+ /** Must match server-shared `AGENT_SERVE_GENERATION_ENV_VAR`. */
166
+ export const AGENT_SERVE_GENERATION_ENV_VAR = "AGENT_SERVE_GENERATION";
167
+
168
+ export function resolveAgentServeDeploymentGeneration(
169
+ explicit?: number
170
+ ): number | undefined {
171
+ if (
172
+ explicit !== undefined &&
173
+ Number.isSafeInteger(explicit) &&
174
+ explicit > 0
175
+ ) {
176
+ return explicit;
177
+ }
178
+ const raw = process.env[AGENT_SERVE_GENERATION_ENV_VAR]?.trim();
179
+ if (raw === undefined || raw.length === 0) {
180
+ return undefined;
181
+ }
182
+ if (!/^\d+$/.test(raw)) {
183
+ return undefined;
184
+ }
185
+ const parsed = Number(raw);
186
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
187
+ return undefined;
188
+ }
189
+ return parsed;
154
190
  }
155
191
 
156
192
  export class CursorRelayCore<TEvent extends { offset: string }> {
@@ -160,6 +196,7 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
160
196
  private consumePromise: Promise<void> | undefined;
161
197
  private readonly offsetStore: RelayOffsetStore;
162
198
  private readonly fetchImpl: typeof fetch;
199
+ private readonly deploymentGeneration: number | undefined;
163
200
  private consumerId = "";
164
201
  /** undefined until the first registration, so a clean first success logs. */
165
202
  private lastRegistrationLog: string | undefined;
@@ -175,6 +212,9 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
175
212
  constructor(private readonly options: CursorRelayCoreOptions<TEvent>) {
176
213
  this.offsetStore = new RelayOffsetStore(options.stateDir);
177
214
  this.fetchImpl = options.fetchImpl ?? fetch;
215
+ this.deploymentGeneration = resolveAgentServeDeploymentGeneration(
216
+ options.deploymentGeneration
217
+ );
178
218
  }
179
219
 
180
220
  start(): void {
@@ -285,6 +325,13 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
285
325
  headers: {
286
326
  authorization: `Bearer ${this.options.apiKey}`,
287
327
  "content-type": "application/json",
328
+ ...(this.deploymentGeneration === undefined
329
+ ? {}
330
+ : {
331
+ [AGENT_SERVE_GENERATION_HEADER]: String(
332
+ this.deploymentGeneration
333
+ ),
334
+ }),
288
335
  },
289
336
  body: JSON.stringify(adapter.buildDeregistrationBody(this.consumerId)),
290
337
  signal: controller.signal,
@@ -322,6 +369,13 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
322
369
  headers: {
323
370
  authorization: `Bearer ${this.options.apiKey}`,
324
371
  ...(body === undefined ? {} : { "content-type": "application/json" }),
372
+ ...(this.deploymentGeneration === undefined
373
+ ? {}
374
+ : {
375
+ [AGENT_SERVE_GENERATION_HEADER]: String(
376
+ this.deploymentGeneration
377
+ ),
378
+ }),
325
379
  },
326
380
  ...(body === undefined ? {} : { body: JSON.stringify(body) }),
327
381
  ...(this.abort === undefined ? {} : { signal: this.abort.signal }),
@@ -514,14 +568,19 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
514
568
  response: Response,
515
569
  backoffMs: number
516
570
  ): Promise<number> {
517
- const isAuthFailure = response.status === 401 || response.status === 403;
518
- const sleepMs = isAuthFailure ? AUTH_FAILURE_BACKOFF_MS : backoffMs;
571
+ // 409 = generation fence (stale resident after redeploy). Same long
572
+ // backoff as auth: retrying every second cannot heal a wrong generation.
573
+ const isAuthOrFenceFailure =
574
+ response.status === 401 ||
575
+ response.status === 403 ||
576
+ response.status === 409;
577
+ const sleepMs = isAuthOrFenceFailure ? AUTH_FAILURE_BACKOFF_MS : backoffMs;
519
578
  const text = await response.text().catch(() => "");
520
579
  this.log(
521
580
  `poll failed: ${response.status} ${text.slice(0, 300)} (retrying in ${Math.round(sleepMs / 1000)}s)`
522
581
  );
523
582
  await this.sleep(sleepMs);
524
- return isAuthFailure
583
+ return isAuthOrFenceFailure
525
584
  ? AUTH_FAILURE_BACKOFF_MS
526
585
  : Math.min(backoffMs * 2, MAX_BACKOFF_MS);
527
586
  }
@@ -209,6 +209,30 @@ export interface SecretInfo {
209
209
  createdAt: string;
210
210
  }
211
211
 
212
+ /**
213
+ * `GET /internal/agent-serve/automations/:uuid/export` response
214
+ * (backend `agentServeAutomationExport.ts`). Name + transport references
215
+ * only — the backend never exports URLs or credential material.
216
+ */
217
+ export interface AutomationExportMcpServer {
218
+ id?: string;
219
+ name: string;
220
+ transportType: "http" | "sse" | "stdio";
221
+ }
222
+
223
+ export interface AutomationExport {
224
+ automation: {
225
+ uuid: string;
226
+ name: string;
227
+ enabled: boolean;
228
+ scope: string;
229
+ webUrl: string;
230
+ };
231
+ /** The Workflow as generic proto3 JSON. */
232
+ workflow: Record<string, unknown>;
233
+ mcpServers: AutomationExportMcpServer[];
234
+ }
235
+
212
236
  // All response schemas are `.passthrough()`: the backend keeps growing the
213
237
  // deployment payload (serviceAccountId, egressAllowedDomains, secret
214
238
  // metadata, ...) and unknown fields must flow through untouched — both so
@@ -333,6 +357,30 @@ const listSecretsResponseSchema = z
333
357
  })
334
358
  .passthrough();
335
359
 
360
+ const automationExportSchema = z
361
+ .object({
362
+ automation: z
363
+ .object({
364
+ uuid: z.string(),
365
+ name: z.string(),
366
+ enabled: z.boolean(),
367
+ scope: z.string(),
368
+ webUrl: z.string(),
369
+ })
370
+ .passthrough(),
371
+ workflow: z.record(z.unknown()),
372
+ mcpServers: z.array(
373
+ z
374
+ .object({
375
+ id: z.string().optional(),
376
+ name: z.string(),
377
+ transportType: z.enum(["http", "sse", "stdio"]),
378
+ })
379
+ .passthrough()
380
+ ),
381
+ })
382
+ .passthrough();
383
+
336
384
  // ============================================================================
337
385
  // Client
338
386
  // ============================================================================
@@ -533,6 +581,36 @@ export class AgentServeDeployClient {
533
581
  return rotatePodCredentialResponseSchema.parse(raw).podCredentialMaskedKey;
534
582
  }
535
583
 
584
+ /**
585
+ * Read an Automation's exported workflow definition for
586
+ * `convert-automation`. Missing, no-access, and gate-off all surface as
587
+ * indistinguishable 404s on the backend, so they map to one message here
588
+ * (the wrong-host 404 hint from {@link mapErrorResponse} passes through).
589
+ */
590
+ async exportAutomation(args: { uuid: string }): Promise<AutomationExport> {
591
+ let raw: unknown;
592
+ try {
593
+ raw = await this.request({
594
+ method: "GET",
595
+ path: `/internal/agent-serve/automations/${encodeURIComponent(args.uuid)}/export`,
596
+ context: { verb: "export automation" },
597
+ });
598
+ } catch (error) {
599
+ if (
600
+ error instanceof DeployApiError &&
601
+ error.status === 404 &&
602
+ !error.message.includes("api.cursor.com")
603
+ ) {
604
+ throw new DeployApiError(
605
+ `Automation ${args.uuid} was not found or you don't have access to it (the agent_serve_mvp feature gate may also be off for your team).`,
606
+ 404
607
+ );
608
+ }
609
+ throw error;
610
+ }
611
+ return automationExportSchema.parse(raw);
612
+ }
613
+
536
614
  /** Full URL of the stable engine alias for a deployment. */
537
615
  aliasUrl(engineAlias: string): string {
538
616
  return `${this.backendUrl}${engineAlias}`;
@@ -420,7 +420,6 @@ async function loadAgent(
420
420
 
421
421
  if (runtime === "cloud") {
422
422
  warnCloudRuntimeLimitations(ctx, agentDir, {
423
- serverTools: tools.filter((tool) => tool.execution === "server").length,
424
423
  agentTools: tools.filter((tool) => tool.execution === "agent").length,
425
424
  skills: skills.length,
426
425
  seedFiles: seedFiles.length,
@@ -428,7 +427,7 @@ async function loadAgent(
428
427
  });
429
428
  }
430
429
 
431
- appendBuiltinTools(ctx, agentDir, tools, config?.builtinTools, runtime);
430
+ appendBuiltinTools(ctx, agentDir, tools, config?.builtinTools);
432
431
 
433
432
  return {
434
433
  name,
@@ -1192,30 +1191,19 @@ async function warnUnsupportedSubagentSlots(
1192
1191
 
1193
1192
  /**
1194
1193
  * Append enabled built-in tools after authored tools. Authored tools with
1195
- * the same name win; built-ins are server tools, so cloud runtimes get a
1196
- * warning instead of tools.
1194
+ * the same name win.
1197
1195
  */
1198
1196
  function appendBuiltinTools(
1199
1197
  ctx: DiscoveryContext,
1200
1198
  agentDir: string,
1201
1199
  tools: DiscoveredTool[],
1202
- config: BuiltinToolsConfig | undefined,
1203
- runtime: AgentRuntime
1200
+ config: BuiltinToolsConfig | undefined
1204
1201
  ): void {
1205
1202
  const builtin = builtinTools(config);
1206
1203
  if (builtin.length === 0) {
1207
1204
  return;
1208
1205
  }
1209
1206
  const agentPath = displayPath(ctx, join(agentDir, "agent.ts"));
1210
- if (runtime === "cloud") {
1211
- ctx.diagnostics.push({
1212
- severity: "warning",
1213
- path: agentPath,
1214
- message:
1215
- "builtinTools are in-process server tools and are not available on cloud agents; they are skipped.",
1216
- });
1217
- return;
1218
- }
1219
1207
  const authored = new Set(tools.map((tool) => tool.name));
1220
1208
  for (const tool of builtin) {
1221
1209
  if (authored.has(tool.name)) {
@@ -1234,7 +1222,6 @@ function warnCloudRuntimeLimitations(
1234
1222
  ctx: DiscoveryContext,
1235
1223
  agentDir: string,
1236
1224
  counts: {
1237
- serverTools: number;
1238
1225
  agentTools: number;
1239
1226
  skills: number;
1240
1227
  seedFiles: number;
@@ -1250,14 +1237,6 @@ function warnCloudRuntimeLimitations(
1250
1237
  'runtime: "cloud" usually needs a `cloud` block (e.g. repos / env). Without it the Cursor cloud API may reject create.',
1251
1238
  });
1252
1239
  }
1253
- if (counts.serverTools > 0) {
1254
- ctx.diagnostics.push({
1255
- severity: "warning",
1256
- path: displayPath(ctx, join(agentDir, "tools")),
1257
- message:
1258
- 'In-process tools (execution: "server", the default) are not available on cloud agents. Use execution: "agent", MCP connections, or tools that already live in the cloud workspace.',
1259
- });
1260
- }
1261
1240
  if (counts.agentTools > 0) {
1262
1241
  ctx.diagnostics.push({
1263
1242
  severity: "warning",
@@ -1647,19 +1626,6 @@ async function appendArtifactTool(
1647
1626
  (await findModuleFile(agentDir, "artifacts")) ??
1648
1627
  join(agentDir, "artifacts.ts")
1649
1628
  );
1650
- // TODO(artifacts-cloud): on cloud runtimes, ship tag_artifact as an
1651
- // execution: "agent" script and have the host fold its action.result
1652
- // stream events into artifact records — agent-tool calls already surface
1653
- // durably in the session stream.
1654
- if (agent.runtime === "cloud") {
1655
- ctx.diagnostics.push({
1656
- severity: "warning",
1657
- path: artifactsPath,
1658
- message:
1659
- "tag_artifact is an in-process server tool (like builtinTools) and is not available on cloud agents; it is skipped.",
1660
- });
1661
- return;
1662
- }
1663
1629
  if (agent.tools.some((t) => t.name === TAG_ARTIFACT_TOOL_NAME)) {
1664
1630
  ctx.diagnostics.push({
1665
1631
  severity: "warning",
@@ -206,7 +206,7 @@ export default memoryHook();
206
206
  };
207
207
  }
208
208
 
209
- function isEexist(error: unknown): boolean {
209
+ export function isEexist(error: unknown): boolean {
210
210
  return (
211
211
  error !== null &&
212
212
  typeof error === "object" &&