@cursor/july 0.1.7 → 0.1.9

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 (210) hide show
  1. package/dist/bin/agent-serve.js +31 -2
  2. package/dist/channels/slack/channel-watch.d.ts +6 -1
  3. package/dist/channels/slack/channel-watch.d.ts.map +1 -1
  4. package/dist/channels/slack/channel-watch.js +22 -1
  5. package/dist/channels/slack/cursor-account.d.ts +4 -0
  6. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  7. package/dist/channels/slack/cursor-account.js +12 -2
  8. package/dist/channels/slack/defaults.d.ts +10 -5
  9. package/dist/channels/slack/defaults.d.ts.map +1 -1
  10. package/dist/channels/slack/defaults.js +39 -17
  11. package/dist/channels/slack/dispatch.d.ts +18 -0
  12. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  13. package/dist/channels/slack/dispatch.js +141 -38
  14. package/dist/channels/slack/inbound.d.ts +13 -3
  15. package/dist/channels/slack/inbound.d.ts.map +1 -1
  16. package/dist/channels/slack/inbound.js +17 -9
  17. package/dist/channels/slack/index.d.ts +4 -0
  18. package/dist/channels/slack/index.d.ts.map +1 -1
  19. package/dist/channels/slack/index.js +4 -0
  20. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  21. package/dist/channels/slack/live-delivery.js +1 -0
  22. package/dist/channels/slack/manifest.d.ts.map +1 -1
  23. package/dist/channels/slack/manifest.js +3 -0
  24. package/dist/channels/slack/nudge-carrier.d.ts +19 -0
  25. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
  26. package/dist/channels/slack/nudge-carrier.js +166 -0
  27. package/dist/channels/slack/nudge-store.d.ts +61 -0
  28. package/dist/channels/slack/nudge-store.d.ts.map +1 -0
  29. package/dist/channels/slack/nudge-store.js +266 -0
  30. package/dist/channels/slack/post-update-delivery.js +318 -0
  31. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  32. package/dist/channels/slack/slack-channel.js +74 -25
  33. package/dist/channels/slack/stop.d.ts +14 -0
  34. package/dist/channels/slack/stop.d.ts.map +1 -0
  35. package/dist/channels/slack/stop.js +22 -0
  36. package/dist/channels/slack/thread-context.d.ts +81 -0
  37. package/dist/channels/slack/thread-context.d.ts.map +1 -0
  38. package/dist/channels/slack/thread-context.js +470 -0
  39. package/dist/channels/slack/types.d.ts +27 -0
  40. package/dist/channels/slack/types.d.ts.map +1 -1
  41. package/dist/docs/404.html +2 -2
  42. package/dist/docs/ab.html +4 -4
  43. package/dist/docs/assets/{app.COTN7wgo.js → app.Ciosf24M.js} +1 -1
  44. package/dist/docs/assets/chunks/@localSearchIndexroot.D154Detj.js +1 -0
  45. package/dist/docs/assets/chunks/{VPLocalSearchBox.BW3TBdT0.js → VPLocalSearchBox.BcRDly7n.js} +1 -1
  46. package/dist/docs/assets/chunks/{theme.BEJW0vE7.js → theme.CMpbjdfx.js} +2 -2
  47. package/dist/docs/assets/{deployment.md.BtfEsc9S.js → deployment.md.DC5-ImcM.js} +1 -1
  48. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.js → example-agents_approval-buddy.md.DFGBYLcc.js} +2 -2
  49. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.lean.js → example-agents_approval-buddy.md.DFGBYLcc.lean.js} +1 -1
  50. package/dist/docs/assets/example-agents_index.md.BPXjUtRB.js +2 -0
  51. package/dist/docs/assets/example-agents_index.md.BPXjUtRB.lean.js +1 -0
  52. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
  53. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
  54. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.js → example-agents_security-reviewer.md.CE9v-aKm.js} +2 -2
  55. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.lean.js → example-agents_security-reviewer.md.CE9v-aKm.lean.js} +1 -1
  56. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
  57. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
  58. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
  59. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
  60. package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.DRTdnFvd.js} +9 -2
  61. package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.DRTdnFvd.lean.js} +1 -1
  62. package/dist/docs/building-with-agents.html +4 -4
  63. package/dist/docs/concepts.html +4 -4
  64. package/dist/docs/deployment.html +6 -6
  65. package/dist/docs/evals.html +4 -4
  66. package/dist/docs/example-agents/approval-buddy.html +5 -5
  67. package/dist/docs/example-agents/benny.html +5 -5
  68. package/dist/docs/example-agents/bugbot.html +5 -5
  69. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  70. package/dist/docs/example-agents/codeowners-review.html +4 -4
  71. package/dist/docs/example-agents/concierge.html +4 -4
  72. package/dist/docs/example-agents/fsd.html +4 -4
  73. package/dist/docs/example-agents/index.html +6 -6
  74. package/dist/docs/example-agents/knowledge-base.html +4 -4
  75. package/dist/docs/example-agents/oncall.html +34 -0
  76. package/dist/docs/example-agents/security-reviewer.html +6 -6
  77. package/dist/docs/example-agents/slack-agent.html +4 -4
  78. package/dist/docs/example-agents/weather-agent.html +4 -4
  79. package/dist/docs/guides/agent-to-agent.html +4 -4
  80. package/dist/docs/guides/cloud-runtime.html +4 -4
  81. package/dist/docs/guides/github.html +4 -4
  82. package/dist/docs/guides/human-in-the-loop.html +4 -4
  83. package/dist/docs/guides/slack.html +7 -6
  84. package/dist/docs/guides/webhooks.html +4 -4
  85. package/dist/docs/hashmap.json +1 -1
  86. package/dist/docs/hillclimbing.html +4 -4
  87. package/dist/docs/index.html +4 -4
  88. package/dist/docs/quickstart.html +4 -4
  89. package/dist/docs/reference/agent-config.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +4 -4
  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/schedules.html +4 -4
  99. package/dist/docs/reference/sessions.html +5 -5
  100. package/dist/docs/reference/skills.html +4 -4
  101. package/dist/docs/reference/subagents.html +4 -4
  102. package/dist/docs/reference/tools.html +4 -4
  103. package/dist/docs/scaffolding-agents.html +4 -4
  104. package/dist/docs/storage.html +13 -6
  105. package/dist/docs/troubleshooting.html +4 -4
  106. package/dist/internal/cli-deploy.d.ts.map +1 -1
  107. package/dist/internal/cli-deploy.js +45 -18
  108. package/dist/internal/cli-mcp.d.ts +64 -0
  109. package/dist/internal/cli-mcp.d.ts.map +1 -0
  110. package/dist/internal/cli-mcp.js +570 -0
  111. package/dist/internal/deploy-client.d.ts +7 -1
  112. package/dist/internal/deploy-client.d.ts.map +1 -1
  113. package/dist/internal/deploy-client.js +3 -0
  114. package/dist/internal/deploy-source.d.ts +3 -0
  115. package/dist/internal/deploy-source.d.ts.map +1 -1
  116. package/dist/internal/deploy-source.js +25 -7
  117. package/dist/internal/discovery.js +1 -1
  118. package/dist/internal/distribution.d.ts.map +1 -1
  119. package/dist/internal/distribution.js +3 -0
  120. package/dist/internal/host-kv.d.ts +17 -0
  121. package/dist/internal/host-kv.d.ts.map +1 -0
  122. package/dist/internal/host-kv.js +77 -0
  123. package/dist/internal/hosting.d.ts +12 -2
  124. package/dist/internal/hosting.d.ts.map +1 -1
  125. package/dist/internal/hosting.js +21 -4
  126. package/dist/internal/json-dir-store.js +100 -0
  127. package/dist/internal/logs-client.d.ts +16 -2
  128. package/dist/internal/logs-client.d.ts.map +1 -1
  129. package/dist/internal/logs-client.js +51 -16
  130. package/dist/internal/server.d.ts.map +1 -1
  131. package/dist/internal/server.js +8 -0
  132. package/dist/internal/session-engine.d.ts +42 -7
  133. package/dist/internal/session-engine.d.ts.map +1 -1
  134. package/dist/internal/session-engine.js +276 -66
  135. package/dist/internal/storage-coordinator.d.ts +18 -1
  136. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  137. package/dist/internal/storage-coordinator.js +43 -0
  138. package/dist/internal/workspace.d.ts +19 -0
  139. package/dist/internal/workspace.d.ts.map +1 -1
  140. package/dist/internal/workspace.js +47 -13
  141. package/dist/playground/assets/{index-72vCOBWO.js → index-C0-QEG4O.js} +42 -42
  142. package/dist/playground/assets/index-z3JY9O2x.css +1 -0
  143. package/dist/playground/index.html +2 -2
  144. package/dist/storage-backends/cursor-hosted.d.ts +65 -0
  145. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -0
  146. package/dist/storage-backends/cursor-hosted.js +203 -0
  147. package/dist/storage-backends/file-kv.d.ts +42 -0
  148. package/dist/storage-backends/file-kv.d.ts.map +1 -0
  149. package/dist/storage-backends/file-kv.js +190 -0
  150. package/dist/storage-backends/postgres-kv.d.ts +82 -0
  151. package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
  152. package/dist/storage-backends/postgres-kv.js +185 -0
  153. package/dist/storage.d.ts +10 -1
  154. package/dist/storage.d.ts.map +1 -1
  155. package/dist/storage.js +10 -1
  156. package/dist/types.d.ts +79 -0
  157. package/dist/types.d.ts.map +1 -1
  158. package/docs/.vitepress/cache/deps/package.json +3 -0
  159. package/docs/deployment.md +7 -3
  160. package/docs/example-agents/approval-buddy.md +1 -1
  161. package/docs/example-agents/index.md +15 -10
  162. package/docs/example-agents/oncall.md +216 -0
  163. package/docs/example-agents/security-reviewer.md +5 -5
  164. package/docs/guides/slack.md +11 -2
  165. package/docs/reference/sessions.md +13 -4
  166. package/docs/storage.md +41 -0
  167. package/package.json +22 -1
  168. package/skills/framework-map/SKILL.md +6 -4
  169. package/src/bin/agent-serve.ts +32 -2
  170. package/src/channels/slack/channel-watch.ts +28 -1
  171. package/src/channels/slack/cursor-account.ts +20 -0
  172. package/src/channels/slack/defaults.ts +51 -20
  173. package/src/channels/slack/dispatch.ts +215 -40
  174. package/src/channels/slack/inbound.ts +30 -10
  175. package/src/channels/slack/index.ts +4 -0
  176. package/src/channels/slack/live-delivery.ts +1 -0
  177. package/src/channels/slack/manifest.ts +3 -0
  178. package/src/channels/slack/nudge-carrier.ts +182 -0
  179. package/src/channels/slack/nudge-store.ts +308 -0
  180. package/src/channels/slack/slack-channel.ts +106 -31
  181. package/src/channels/slack/stop.ts +25 -0
  182. package/src/channels/slack/thread-context.ts +572 -0
  183. package/src/channels/slack/types.ts +27 -0
  184. package/src/internal/cli-deploy.ts +58 -20
  185. package/src/internal/cli-mcp.ts +708 -0
  186. package/src/internal/deploy-client.ts +11 -0
  187. package/src/internal/deploy-source.ts +24 -6
  188. package/src/internal/discovery.ts +1 -1
  189. package/src/internal/distribution.ts +3 -0
  190. package/src/internal/host-kv.ts +85 -0
  191. package/src/internal/hosting.ts +24 -4
  192. package/src/internal/logs-client.ts +67 -21
  193. package/src/internal/server.ts +31 -0
  194. package/src/internal/session-engine.ts +379 -61
  195. package/src/internal/storage-coordinator.ts +41 -0
  196. package/src/internal/workspace.ts +48 -11
  197. package/src/storage-backends/cursor-hosted.ts +246 -0
  198. package/src/storage-backends/file-kv.ts +221 -0
  199. package/src/storage-backends/postgres-kv.ts +253 -0
  200. package/src/storage.ts +11 -1
  201. package/src/types.ts +83 -0
  202. package/dist/docs/assets/chunks/@localSearchIndexroot.B7UcKvIn.js +0 -1
  203. package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
  204. package/dist/docs/assets/example-agents_index.md.CRqJlnIf.lean.js +0 -1
  205. package/dist/internal/persistence-coordinator.d.ts +0 -127
  206. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  207. package/dist/persistence.d.ts +0 -184
  208. package/dist/persistence.d.ts.map +0 -1
  209. package/dist/playground/assets/index-BjnMwYoR.css +0 -1
  210. /package/dist/docs/assets/{deployment.md.BtfEsc9S.lean.js → deployment.md.DC5-ImcM.lean.js} +0 -0
@@ -31,7 +31,9 @@ export type DeploymentStatus =
31
31
  export type DeploymentProgress =
32
32
  | "queued"
33
33
  | "preparing"
34
+ | "creating"
34
35
  | "building"
36
+ | "starting"
35
37
  | "finalizing"
36
38
  | "running"
37
39
  | "failed"
@@ -135,6 +137,12 @@ export interface Deployment {
135
137
  * backends; older responses omit it.
136
138
  */
137
139
  progress?: DeploymentProgress;
140
+ /**
141
+ * Live step while the deploy converges (e.g. "Checking out source…").
142
+ * Null when running / stopped or when only a failure is on the row.
143
+ * Present on newer backends; older responses omit it.
144
+ */
145
+ statusMessage?: string | null;
138
146
  generation: number;
139
147
  observedGeneration?: number | null;
140
148
  engineGeneration?: number | null;
@@ -218,7 +226,9 @@ const deploymentSchema = z.object({
218
226
  .enum([
219
227
  "queued",
220
228
  "preparing",
229
+ "creating",
221
230
  "building",
231
+ "starting",
222
232
  "finalizing",
223
233
  "running",
224
234
  "failed",
@@ -227,6 +237,7 @@ const deploymentSchema = z.object({
227
237
  "degraded",
228
238
  ])
229
239
  .optional(),
240
+ statusMessage: z.string().nullable().optional(),
230
241
  generation: z.number(),
231
242
  observedGeneration: z.number().nullable().optional(),
232
243
  engineGeneration: z.number().nullable().optional(),
@@ -39,17 +39,35 @@ const defaultGitRunner: GitRunner = async (args, cwd) => {
39
39
  * Normalize a git remote URL to the https form the control plane accepts
40
40
  * (`https://host/owner/repo`, no `.git` suffix). Returns undefined for
41
41
  * unrecognized shapes.
42
+ *
43
+ * Accepts HTTPS, `git@host:path`, GitHub org-scoped SSH
44
+ * (`org-123@github.com:owner/repo.git`), and `ssh://user@host[:port]/path`.
42
45
  */
43
46
  export function httpsGitRepoUrlFromRemote(remote: string): string | undefined {
44
47
  let url = remote.trim();
45
48
  if (url === "") {
46
49
  return undefined;
47
50
  }
48
- if (url.startsWith("git@")) {
49
- // git@host:owner/repo.git https://host/owner/repo.git
50
- url = `https://${url.slice("git@".length).replace(":", "/")}`;
51
- } else if (url.startsWith("ssh://git@")) {
52
- url = `https://${url.slice("ssh://git@".length)}`;
51
+
52
+ // SCP-like SSH: user@host:owner/repo(.git) includes git@ and GitHub
53
+ // org deploy keys (org-NNNN@github.com:…).
54
+ const scpMatch = /^([^@/\s]+)@([^:/\s]+):(.+)$/.exec(url);
55
+ if (scpMatch !== null) {
56
+ const host = scpMatch[2]!;
57
+ const path = scpMatch[3]!;
58
+ url = `https://${host}/${path}`;
59
+ } else if (/^ssh:\/\//i.test(url)) {
60
+ // ssh://user@host[:port]/owner/repo → https://host/owner/repo
61
+ try {
62
+ const ssh = new URL(url);
63
+ const path = ssh.pathname.replace(/^\/+/, "");
64
+ if (ssh.hostname === "" || path === "") {
65
+ return undefined;
66
+ }
67
+ url = `https://${ssh.hostname}/${path}`;
68
+ } catch {
69
+ return undefined;
70
+ }
53
71
  } else if (!/^https?:\/\//i.test(url)) {
54
72
  return undefined;
55
73
  }
@@ -67,7 +85,7 @@ export function httpsGitRepoUrlFromRemote(remote: string): string | undefined {
67
85
  if (path === "" || !path.includes("/")) {
68
86
  return undefined;
69
87
  }
70
- // Use hostname (not host) so ssh://git@github.com:22/… does not keep :22.
88
+ // Use hostname (not host) so ssh://…@github.com:22/… does not keep :22.
71
89
  return `https://${parsed.hostname}/${path}`;
72
90
  }
73
91
 
@@ -454,7 +454,7 @@ function resolveHosting(
454
454
  warn(`hosting.secretNames: ${nameError}`);
455
455
  } else if (isReservedSecretName(name)) {
456
456
  warn(
457
- `hosting.secretNames: ${JSON.stringify(name)} is platform-reserved (CURSOR_*) and injected automatically; it cannot be set with \`secrets set\`.`
457
+ `hosting.secretNames: ${JSON.stringify(name)} is platform-managed (CURSOR_* / Cursor-hosted storage) and injected automatically; omit it from secretNames.`
458
458
  );
459
459
  }
460
460
  }
@@ -103,4 +103,7 @@ export const AUTHORING_ENTRY_FILES: ReadonlyArray<
103
103
  ["evals", "evals.js"],
104
104
  ["ab", "ab.js"],
105
105
  ["storage", "storage.js"],
106
+ ["storage/file-kv", "storage-backends/file-kv.js"],
107
+ ["storage/postgres-kv", "storage-backends/postgres-kv.js"],
108
+ ["storage/cursor-hosted", "storage-backends/cursor-hosted.js"],
106
109
  ];
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Author-facing durable JSON KV for {@link HostContext.kv}.
3
+ *
4
+ * When `defineStorage` is authored and exposes `get`, values live in that
5
+ * sink under {@link storageKeys.kv}. Otherwise they land as one JSON file
6
+ * per key under `--state-root/kv` (local dogfood / no durable plug-in).
7
+ */
8
+
9
+ import { randomUUID } from "node:crypto";
10
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
11
+ import { join } from "node:path";
12
+ import { storageKeys } from "../storage.js";
13
+ import type { HostKvApi, JsonValue } from "../types.js";
14
+ import type { StorageCoordinator } from "./storage-coordinator.js";
15
+
16
+ export interface CreateHostKvApiOptions {
17
+ agentName: string;
18
+ stateRoot: string;
19
+ storage?: StorageCoordinator;
20
+ }
21
+
22
+ /** Build the host KV API for one agent process. */
23
+ export function createHostKvApi(options: CreateHostKvApiOptions): HostKvApi {
24
+ const storage = options.storage;
25
+ if (storage?.supportsAuthorKv === true) {
26
+ return {
27
+ get: (key) => storage.kvGet(normalizeAuthorKey(key)),
28
+ put: (key, value) => storage.kvPut(normalizeAuthorKey(key), value),
29
+ delete: (key) => storage.kvDelete(normalizeAuthorKey(key)),
30
+ };
31
+ }
32
+ return createFilesystemKvApi({
33
+ agentName: options.agentName,
34
+ stateRoot: options.stateRoot,
35
+ });
36
+ }
37
+
38
+ function createFilesystemKvApi(options: {
39
+ agentName: string;
40
+ stateRoot: string;
41
+ }): HostKvApi {
42
+ const dir = join(options.stateRoot, "kv");
43
+ const filePath = (authorKey: string): string =>
44
+ join(
45
+ dir,
46
+ `${encodeURIComponent(storageKeys.kv(options.agentName, authorKey))}.json`
47
+ );
48
+
49
+ return {
50
+ async get(key): Promise<JsonValue | undefined> {
51
+ const authorKey = normalizeAuthorKey(key);
52
+ try {
53
+ return JSON.parse(
54
+ await readFile(filePath(authorKey), "utf8")
55
+ ) as JsonValue;
56
+ } catch (error) {
57
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
58
+ return undefined;
59
+ }
60
+ throw error;
61
+ }
62
+ },
63
+
64
+ async put(key, value): Promise<void> {
65
+ const authorKey = normalizeAuthorKey(key);
66
+ await mkdir(dir, { recursive: true });
67
+ const target = filePath(authorKey);
68
+ const tmp = join(dir, `.tmp-${randomUUID()}`);
69
+ await writeFile(tmp, JSON.stringify(value), "utf8");
70
+ await rename(tmp, target);
71
+ },
72
+
73
+ async delete(key): Promise<void> {
74
+ const authorKey = normalizeAuthorKey(key);
75
+ await rm(filePath(authorKey), { force: true });
76
+ },
77
+ };
78
+ }
79
+
80
+ function normalizeAuthorKey(key: string): string {
81
+ if (typeof key !== "string" || key.trim() === "") {
82
+ throw new Error("host.kv key must be a non-empty string");
83
+ }
84
+ return key;
85
+ }
@@ -18,6 +18,17 @@ export const MAX_SECRET_NAME_LENGTH = 64;
18
18
  /** Secret names the platform injects; user writes are rejected. */
19
19
  export const RESERVED_SECRET_NAME_PREFIX = "CURSOR_";
20
20
 
21
+ /**
22
+ * Additional platform-managed env names for Cursor-hosted durable storage
23
+ * (Bugbot `agent_serve_*`). Mirrored from
24
+ * `@cursor/july/storage/cursor-hosted` so validate/secrets CLI stay in sync
25
+ * without importing the storage backend into the CLI graph.
26
+ */
27
+ export const PLATFORM_HOSTED_STORAGE_SECRET_NAMES = [
28
+ "BUGBOTDB_URL",
29
+ "AGENT_SERVE_DEPLOYMENT_ID",
30
+ ] as const;
31
+
21
32
  const SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
22
33
 
23
34
  // Lowercase hostname with >=2 labels, an alphabetic TLD, and at most one
@@ -49,14 +60,23 @@ export function validateSecretName(name: string): string | undefined {
49
60
  return undefined;
50
61
  }
51
62
 
52
- /** True when the platform reserves this name (`CURSOR_*`); writes are rejected. */
63
+ /**
64
+ * True when the platform manages this name (`CURSOR_*` or hosted-storage
65
+ * env vars); user writes / deletes are rejected.
66
+ */
53
67
  export function isReservedSecretName(name: string): boolean {
54
- return name.startsWith(RESERVED_SECRET_NAME_PREFIX);
68
+ if (name.startsWith(RESERVED_SECRET_NAME_PREFIX)) {
69
+ return true;
70
+ }
71
+ const upper = name.toUpperCase();
72
+ return (PLATFORM_HOSTED_STORAGE_SECRET_NAMES as readonly string[]).some(
73
+ (managed) => managed === upper
74
+ );
55
75
  }
56
76
 
57
77
  /**
58
78
  * Human error for a secret name a user is trying to write or delete:
59
- * grammar plus the reserved `CURSOR_*` prefix.
79
+ * grammar plus platform-managed names (`CURSOR_*`, hosted storage).
60
80
  */
61
81
  export function validateSettableSecretName(name: string): string | undefined {
62
82
  const nameError = validateSecretName(name);
@@ -64,7 +84,7 @@ export function validateSettableSecretName(name: string): string | undefined {
64
84
  return nameError;
65
85
  }
66
86
  if (isReservedSecretName(name)) {
67
- return `Secret name ${JSON.stringify(name)} is platform-reserved (CURSOR_*); the platform injects those values automatically.`;
87
+ return `Secret name ${JSON.stringify(name)} is platform-managed; the platform injects CURSOR_* and Cursor-hosted storage values automatically.`;
68
88
  }
69
89
  return undefined;
70
90
  }
@@ -35,7 +35,9 @@ export type LogsProgress =
35
35
  | DeploymentProgress
36
36
  | "queued"
37
37
  | "preparing"
38
+ | "creating"
38
39
  | "building"
40
+ | "starting"
39
41
  | "finalizing"
40
42
  | "running"
41
43
  | "failed"
@@ -46,7 +48,9 @@ export type LogsProgress =
46
48
  const PROGRESS_MESSAGES: Readonly<Record<LogsProgress, string>> = {
47
49
  queued: "Waiting to start…",
48
50
  preparing: "Preparing runtime…",
49
- building: "Building and starting agent…",
51
+ creating: "Creating runtime…",
52
+ building: "Checking out source and installing dependencies…",
53
+ starting: "Starting agent and waiting for health…",
50
54
  finalizing: "Finishing deployment…",
51
55
  running: "Deployment running",
52
56
  failed: "Deploy failed",
@@ -55,9 +59,15 @@ const PROGRESS_MESSAGES: Readonly<Record<LogsProgress, string>> = {
55
59
  degraded: "Deployment degraded",
56
60
  };
57
61
 
62
+ /**
63
+ * Customer-facing line for the current deploy step. Prefer the live
64
+ * `statusMessage` from the API when present; otherwise the progress
65
+ * vocabulary. Failed/degraded always append sanitized `lastError`.
66
+ */
58
67
  export function progressMessage(
59
68
  progress: LogsProgress,
60
- lastError?: string | null
69
+ lastError?: string | null,
70
+ statusMessage?: string | null
61
71
  ): string {
62
72
  if (progress === "failed") {
63
73
  const detail =
@@ -73,6 +83,13 @@ export function progressMessage(
73
83
  : `: ${sanitizeCustomerError(lastError)}`;
74
84
  return `Deployment degraded${detail}`;
75
85
  }
86
+ if (
87
+ statusMessage !== null &&
88
+ statusMessage !== undefined &&
89
+ statusMessage.trim() !== ""
90
+ ) {
91
+ return statusMessage.trim();
92
+ }
76
93
  return PROGRESS_MESSAGES[progress];
77
94
  }
78
95
 
@@ -85,7 +102,38 @@ export function sanitizeCustomerError(message: string): string {
85
102
  .replace(/\bcursorvm\.com\b/gi, "host")
86
103
  .replace(/\bx-anyrun-[a-z0-9-]+\b/gi, "header")
87
104
  .replace(/\bPROVISIONING\b/g, "building")
88
- .replace(/\bPLANNED\b/g, "preparing");
105
+ .replace(/\bPLANNED\b/g, "preparing")
106
+ .replace(/\bgitCloneCheckout\b/gi, "git clone")
107
+ .replace(/\bgitFetchCheckout\b/gi, "git fetch")
108
+ .replace(/\bgitCheckout:clone\b/gi, "checkout")
109
+ .replace(/\bgitCheckout:fetch\b/gi, "checkout");
110
+ }
111
+
112
+ /**
113
+ * One-line label for `agentkit deploy` polling (TTY spinner / non-TTY
114
+ * transitions). Uses statusMessage → progress → raw status.
115
+ */
116
+ export function deployProgressLabel(detail: {
117
+ status: string;
118
+ progress?: DeploymentProgress;
119
+ statusMessage?: string | null;
120
+ }): string {
121
+ if (
122
+ detail.statusMessage !== null &&
123
+ detail.statusMessage !== undefined &&
124
+ detail.statusMessage.trim() !== ""
125
+ ) {
126
+ return detail.statusMessage
127
+ .trim()
128
+ .replace(/…$/, "")
129
+ .replace(/\.\.\.$/, "");
130
+ }
131
+ if (detail.progress !== undefined) {
132
+ return PROGRESS_MESSAGES[detail.progress]
133
+ .replace(/…$/, "")
134
+ .replace(/\.\.\.$/, "");
135
+ }
136
+ return detail.status;
89
137
  }
90
138
 
91
139
  export async function fetchLogs(args: {
@@ -241,7 +289,7 @@ async function followProdLogs(args: {
241
289
  );
242
290
  });
243
291
 
244
- let lastProgress: string | undefined;
292
+ let lastEmittedKey: string | undefined;
245
293
  const slugHint = args.slug !== undefined && args.slug !== "" ? args.slug : "";
246
294
  if (args.once !== true) {
247
295
  const label = slugHint !== "" ? slugHint : "deployment";
@@ -260,33 +308,32 @@ async function followProdLogs(args: {
260
308
  }
261
309
 
262
310
  const progress = resolveProgress(detail);
311
+ const message = progressMessage(
312
+ progress,
313
+ detail.lastError,
314
+ detail.statusMessage
315
+ );
316
+ const emitKey = `${progress}\0${message}`;
263
317
 
264
318
  if (progress === "failed") {
265
- const message = progressMessage(progress, detail.lastError);
266
- if (progress !== lastProgress) {
267
- lastProgress = progress;
319
+ if (emitKey !== lastEmittedKey) {
320
+ lastEmittedKey = emitKey;
268
321
  emitProgress(args.write, args.json === true, progress, message);
269
322
  }
270
323
  throw new Error(`Deployment "${detail.slug}" failed.`);
271
324
  }
272
325
  if (progress === "stopped") {
273
- const message = progressMessage(progress, detail.lastError);
274
- if (progress !== lastProgress) {
275
- lastProgress = progress;
326
+ if (emitKey !== lastEmittedKey) {
327
+ lastEmittedKey = emitKey;
276
328
  emitProgress(args.write, args.json === true, progress, message);
277
329
  }
278
330
  throw new Error(`Deployment "${detail.slug}" is stopped.`);
279
331
  }
280
332
  if (progress === "running" || progress === "degraded") {
281
333
  // Hand off to runtime logs. Print degraded once; skip a "running" line.
282
- if (progress === "degraded" && progress !== lastProgress) {
283
- lastProgress = progress;
284
- emitProgress(
285
- args.write,
286
- args.json === true,
287
- progress,
288
- progressMessage(progress, detail.lastError)
289
- );
334
+ if (progress === "degraded" && emitKey !== lastEmittedKey) {
335
+ lastEmittedKey = emitKey;
336
+ emitProgress(args.write, args.json === true, progress, message);
290
337
  }
291
338
  const runtime = await resolveRuntime();
292
339
  await followRuntimeLogs({
@@ -306,9 +353,8 @@ async function followProdLogs(args: {
306
353
  return;
307
354
  }
308
355
 
309
- const message = progressMessage(progress, detail.lastError);
310
- if (progress !== lastProgress) {
311
- lastProgress = progress;
356
+ if (emitKey !== lastEmittedKey) {
357
+ lastEmittedKey = emitKey;
312
358
  emitProgress(args.write, args.json === true, progress, message);
313
359
  }
314
360
 
@@ -423,6 +423,17 @@ export async function startServer(
423
423
  },
424
424
  resolveApproval: (sessionId, callId, decision, auth) =>
425
425
  engine.resolveApproval(sessionId, callId, decision, auth),
426
+ hasContinuationSession: (continuationToken) =>
427
+ engine.hasContinuationSession(channel.id, continuationToken),
428
+ isContinuationBusy: (continuationToken) =>
429
+ engine.isContinuationBusy(channel.id, continuationToken),
430
+ getContinuationLastBotMessageTs: (continuationToken) =>
431
+ engine.getContinuationLastBotMessageTs(
432
+ channel.id,
433
+ continuationToken
434
+ ),
435
+ interruptContinuation: (continuationToken) =>
436
+ engine.interruptContinuation(channel.id, continuationToken),
426
437
  logger,
427
438
  };
428
439
  try {
@@ -1624,6 +1635,26 @@ async function startCursorSlackRelay(input: {
1624
1635
  envelope,
1625
1636
  send: args.send,
1626
1637
  host: args.host,
1638
+ hasContinuationSession: (continuationToken) =>
1639
+ target.runtime.engine.hasContinuationSession(
1640
+ target.channelId,
1641
+ continuationToken
1642
+ ),
1643
+ isContinuationBusy: (continuationToken) =>
1644
+ target.runtime.engine.isContinuationBusy(
1645
+ target.channelId,
1646
+ continuationToken
1647
+ ),
1648
+ getContinuationLastBotMessageTs: (continuationToken) =>
1649
+ target.runtime.engine.getContinuationLastBotMessageTs(
1650
+ target.channelId,
1651
+ continuationToken
1652
+ ),
1653
+ interruptContinuation: (continuationToken) =>
1654
+ target.runtime.engine.interruptContinuation(
1655
+ target.channelId,
1656
+ continuationToken
1657
+ ),
1627
1658
  });
1628
1659
  },
1629
1660
  });