@cursor/july 0.1.100 → 0.1.101

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 (254) hide show
  1. package/dist/bin/agent-serve.js +3 -2
  2. package/dist/channels/deployments/deployments-channel.d.ts +41 -5
  3. package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
  4. package/dist/channels/deployments/deployments-channel.js +88 -8
  5. package/dist/channels/deployments/types.d.ts +4 -3
  6. package/dist/channels/deployments/types.d.ts.map +1 -1
  7. package/dist/channels/origin/defaults.d.ts +43 -0
  8. package/dist/channels/origin/defaults.d.ts.map +1 -0
  9. package/dist/channels/origin/defaults.js +111 -0
  10. package/dist/channels/origin/index.d.ts +2 -0
  11. package/dist/channels/origin/index.d.ts.map +1 -1
  12. package/dist/channels/origin/index.js +2 -0
  13. package/dist/channels/origin/origin-channel.d.ts +2 -1
  14. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  15. package/dist/channels/origin/origin-channel.js +24 -3
  16. package/dist/channels/origin/state.d.ts +32 -0
  17. package/dist/channels/origin/state.d.ts.map +1 -0
  18. package/dist/channels/origin/state.js +23 -0
  19. package/dist/channels/origin/types.d.ts +23 -1
  20. package/dist/channels/origin/types.d.ts.map +1 -1
  21. package/dist/channels/slack/api.d.ts.map +1 -1
  22. package/dist/channels/slack/api.js +2 -1
  23. package/dist/channels/slack/code-fences.d.ts +17 -0
  24. package/dist/channels/slack/code-fences.d.ts.map +1 -0
  25. package/dist/channels/slack/code-fences.js +168 -0
  26. package/dist/channels/slack/index.d.ts +1 -0
  27. package/dist/channels/slack/index.d.ts.map +1 -1
  28. package/dist/channels/slack/index.js +1 -0
  29. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  30. package/dist/channels/slack/live-delivery.js +11 -4
  31. package/dist/connections.d.ts +18 -2
  32. package/dist/connections.d.ts.map +1 -1
  33. package/dist/connections.js +18 -2
  34. package/dist/docs/404.html +2 -2
  35. package/dist/docs/ab.html +4 -4
  36. package/dist/docs/assets/{app.DZ1e0Ycq.js → app.BASO9Kih.js} +1 -1
  37. package/dist/docs/assets/chunks/@localSearchIndexroot.DbFXINq2.js +1 -0
  38. package/dist/docs/assets/chunks/{VPLocalSearchBox.l8omwc6D.js → VPLocalSearchBox.DwkqxoE0.js} +1 -1
  39. package/dist/docs/assets/chunks/{theme.JVcD6gel.js → theme.CCil_13e.js} +2 -2
  40. package/dist/docs/assets/{reference_connections.md.BiGoBAk2.js → reference_connections.md.Di6jJAXF.js} +12 -2
  41. package/dist/docs/assets/{reference_connections.md.BiGoBAk2.lean.js → reference_connections.md.Di6jJAXF.lean.js} +1 -1
  42. package/dist/docs/assets/{reference_http-api.md.BEJx9XVj.js → reference_http-api.md.oySXBO8o.js} +1 -1
  43. package/dist/docs/building-with-agents.html +4 -4
  44. package/dist/docs/concepts.html +4 -4
  45. package/dist/docs/deployment.html +4 -4
  46. package/dist/docs/evals.html +4 -4
  47. package/dist/docs/guides/agent-to-agent.html +4 -4
  48. package/dist/docs/guides/cloud-runtime.html +4 -4
  49. package/dist/docs/guides/convert-automation.html +4 -4
  50. package/dist/docs/guides/github.html +4 -4
  51. package/dist/docs/guides/human-in-the-loop.html +4 -4
  52. package/dist/docs/guides/mcp-oauth.html +4 -4
  53. package/dist/docs/guides/opentelemetry.html +4 -4
  54. package/dist/docs/guides/slack.html +4 -4
  55. package/dist/docs/guides/webhooks.html +4 -4
  56. package/dist/docs/hashmap.json +1 -1
  57. package/dist/docs/hillclimbing.html +4 -4
  58. package/dist/docs/index.html +4 -4
  59. package/dist/docs/llms-full.txt +32 -1
  60. package/dist/docs/quickstart.html +4 -4
  61. package/dist/docs/reference/agent-config.html +4 -4
  62. package/dist/docs/reference/artifacts.html +4 -4
  63. package/dist/docs/reference/channels.html +4 -4
  64. package/dist/docs/reference/cli.html +4 -4
  65. package/dist/docs/reference/connections.html +16 -6
  66. package/dist/docs/reference/connections.md +31 -0
  67. package/dist/docs/reference/hooks.html +4 -4
  68. package/dist/docs/reference/http-api.html +6 -6
  69. package/dist/docs/reference/http-api.md +1 -1
  70. package/dist/docs/reference/instructions.html +4 -4
  71. package/dist/docs/reference/playground.html +4 -4
  72. package/dist/docs/reference/project-layout.html +4 -4
  73. package/dist/docs/reference/prompt.html +4 -4
  74. package/dist/docs/reference/schedules.html +4 -4
  75. package/dist/docs/reference/sessions.html +4 -4
  76. package/dist/docs/reference/skills.html +4 -4
  77. package/dist/docs/reference/subagents.html +4 -4
  78. package/dist/docs/reference/tools.html +4 -4
  79. package/dist/docs/scaffolding-agents.html +4 -4
  80. package/dist/docs/skills/ab.html +4 -4
  81. package/dist/docs/skills/create-agent.html +4 -4
  82. package/dist/docs/skills/debug.html +4 -4
  83. package/dist/docs/skills/evals.html +4 -4
  84. package/dist/docs/skills/framework-map.html +4 -4
  85. package/dist/docs/skills/github.html +4 -4
  86. package/dist/docs/skills/hillclimb.html +4 -4
  87. package/dist/docs/skills/index.html +4 -4
  88. package/dist/docs/skills/mcp-auth.html +4 -4
  89. package/dist/docs/skills/otel.html +4 -4
  90. package/dist/docs/skills/setup-slack.html +4 -4
  91. package/dist/docs/storage.html +4 -4
  92. package/dist/docs/templates/agentic-owners.html +4 -4
  93. package/dist/docs/templates/agents-md.html +4 -4
  94. package/dist/docs/templates/code-wiki.html +4 -4
  95. package/dist/docs/templates/demo.html +4 -4
  96. package/dist/docs/templates/pr-autofixer.html +4 -4
  97. package/dist/docs/templates/security-help.html +4 -4
  98. package/dist/docs/templates/security-reviewer.html +4 -4
  99. package/dist/docs/templates/triage.html +4 -4
  100. package/dist/docs/troubleshooting.html +4 -4
  101. package/dist/extensions/cursor-cloud-agents/extension.d.ts +55 -0
  102. package/dist/extensions/cursor-cloud-agents/extension.d.ts.map +1 -0
  103. package/dist/extensions/cursor-cloud-agents/extension.js +64 -0
  104. package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts +30 -0
  105. package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts.map +1 -0
  106. package/dist/extensions/cursor-cloud-agents/lib/cloud.js +45 -0
  107. package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts +25 -0
  108. package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts.map +1 -0
  109. package/dist/extensions/cursor-cloud-agents/lib/fleet.js +85 -0
  110. package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts +15 -0
  111. package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts.map +1 -0
  112. package/dist/extensions/cursor-cloud-agents/lib/repos.js +55 -0
  113. package/dist/extensions/cursor-cloud-agents/skills/handoff.md +27 -0
  114. package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts +4 -0
  115. package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts.map +1 -0
  116. package/dist/extensions/cursor-cloud-agents/tools/follow_up.js +37 -0
  117. package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts +4 -0
  118. package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts.map +1 -0
  119. package/dist/extensions/cursor-cloud-agents/tools/launch.js +83 -0
  120. package/dist/extensions/cursor-cloud-agents/tools/list.d.ts +4 -0
  121. package/dist/extensions/cursor-cloud-agents/tools/list.d.ts.map +1 -0
  122. package/dist/extensions/cursor-cloud-agents/tools/list.js +36 -0
  123. package/dist/extensions/cursor-cloud-agents/tools/status.d.ts +4 -0
  124. package/dist/extensions/cursor-cloud-agents/tools/status.d.ts.map +1 -0
  125. package/dist/extensions/cursor-cloud-agents/tools/status.js +41 -0
  126. package/dist/extensions.d.ts +49 -0
  127. package/dist/extensions.d.ts.map +1 -0
  128. package/dist/extensions.js +75 -0
  129. package/dist/index.d.ts +2 -1
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +2 -1
  132. package/dist/internal/advertise-tools.d.ts +4 -1
  133. package/dist/internal/advertise-tools.d.ts.map +1 -1
  134. package/dist/internal/advertise-tools.js +20 -1
  135. package/dist/internal/as-of.d.ts +18 -0
  136. package/dist/internal/as-of.d.ts.map +1 -1
  137. package/dist/internal/as-of.js +131 -0
  138. package/dist/internal/cli-deploy.d.ts.map +1 -1
  139. package/dist/internal/cli-deploy.js +64 -19
  140. package/dist/internal/continuation-identity.d.ts.map +1 -1
  141. package/dist/internal/continuation-identity.js +3 -1
  142. package/dist/internal/cursor/hosted-deployment-secrets.d.ts +16 -0
  143. package/dist/internal/cursor/hosted-deployment-secrets.d.ts.map +1 -0
  144. package/dist/internal/cursor/hosted-deployment-secrets.js +61 -0
  145. package/dist/internal/cursor-deployment-relay.d.ts +15 -1
  146. package/dist/internal/cursor-deployment-relay.d.ts.map +1 -1
  147. package/dist/internal/cursor-deployment-relay.js +46 -7
  148. package/dist/internal/deploy-client.d.ts +23 -0
  149. package/dist/internal/deploy-client.d.ts.map +1 -1
  150. package/dist/internal/deploy-client.js +54 -12
  151. package/dist/internal/discovery.d.ts.map +1 -1
  152. package/dist/internal/discovery.js +415 -120
  153. package/dist/internal/distribution.d.ts.map +1 -1
  154. package/dist/internal/distribution.js +5 -0
  155. package/dist/internal/framework-file-storage.d.ts +3 -3
  156. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  157. package/dist/internal/framework-file-storage.js +34 -5
  158. package/dist/internal/hosted-delivery.d.ts +3 -1
  159. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  160. package/dist/internal/hosted-delivery.js +19 -32
  161. package/dist/internal/hosted-execution-diag.d.ts +5 -0
  162. package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
  163. package/dist/internal/hosted-execution-diag.js +5 -0
  164. package/dist/internal/hosted-execution-flush.d.ts +46 -0
  165. package/dist/internal/hosted-execution-flush.d.ts.map +1 -0
  166. package/dist/internal/hosted-execution-flush.js +81 -0
  167. package/dist/internal/mcp-endpoint.js +1 -0
  168. package/dist/internal/mcp-host.d.ts +2 -1
  169. package/dist/internal/mcp-host.d.ts.map +1 -1
  170. package/dist/internal/mcp-host.js +35 -15
  171. package/dist/internal/review-comments.d.ts +1 -1
  172. package/dist/internal/review-comments.d.ts.map +1 -1
  173. package/dist/internal/review-comments.js +4 -4
  174. package/dist/internal/server.d.ts +3 -0
  175. package/dist/internal/server.d.ts.map +1 -1
  176. package/dist/internal/server.js +79 -104
  177. package/dist/internal/session-engine.d.ts +9 -7
  178. package/dist/internal/session-engine.d.ts.map +1 -1
  179. package/dist/internal/session-engine.js +45 -34
  180. package/dist/internal/session-run-log.d.ts +2 -1
  181. package/dist/internal/session-run-log.d.ts.map +1 -1
  182. package/dist/internal/session-run-log.js +2 -1
  183. package/dist/internal/start-log-store.d.ts +60 -0
  184. package/dist/internal/start-log-store.d.ts.map +1 -0
  185. package/dist/internal/start-log-store.js +151 -0
  186. package/dist/internal/tool-policy.d.ts +8 -0
  187. package/dist/internal/tool-policy.d.ts.map +1 -1
  188. package/dist/internal/tool-policy.js +19 -7
  189. package/dist/playground/assets/{index-DVs98vPL.js → index-BnNN1-EK.js} +45 -45
  190. package/dist/playground/assets/index-DBQCwFp0.css +1 -0
  191. package/dist/playground/index.html +2 -2
  192. package/dist/storage-protocol.d.ts +24 -0
  193. package/dist/storage-protocol.d.ts.map +1 -1
  194. package/dist/storage-protocol.js +33 -0
  195. package/dist/storage.d.ts +1 -1
  196. package/dist/storage.d.ts.map +1 -1
  197. package/dist/storage.js +1 -1
  198. package/dist/types.d.ts +118 -6
  199. package/dist/types.d.ts.map +1 -1
  200. package/dist/types.js +29 -1
  201. package/docs/reference/connections.md +31 -0
  202. package/docs/reference/http-api.md +1 -1
  203. package/package.json +18 -4
  204. package/src/bin/agent-serve.ts +11 -2
  205. package/src/channels/deployments/deployments-channel.ts +117 -8
  206. package/src/channels/deployments/types.ts +4 -3
  207. package/src/channels/origin/defaults.ts +143 -0
  208. package/src/channels/origin/index.ts +2 -0
  209. package/src/channels/origin/origin-channel.ts +37 -1
  210. package/src/channels/origin/state.ts +54 -0
  211. package/src/channels/origin/types.ts +24 -0
  212. package/src/channels/slack/api.ts +2 -1
  213. package/src/channels/slack/code-fences.ts +183 -0
  214. package/src/channels/slack/index.ts +1 -0
  215. package/src/channels/slack/live-delivery.ts +16 -13
  216. package/src/connections.ts +18 -1
  217. package/src/extensions/cursor-cloud-agents/extension.ts +118 -0
  218. package/src/extensions/cursor-cloud-agents/lib/cloud.ts +79 -0
  219. package/src/extensions/cursor-cloud-agents/lib/fleet.ts +109 -0
  220. package/src/extensions/cursor-cloud-agents/lib/repos.ts +81 -0
  221. package/src/extensions/cursor-cloud-agents/skills/handoff.md +27 -0
  222. package/src/extensions/cursor-cloud-agents/tools/follow_up.ts +29 -0
  223. package/src/extensions/cursor-cloud-agents/tools/launch.ts +84 -0
  224. package/src/extensions/cursor-cloud-agents/tools/list.ts +31 -0
  225. package/src/extensions/cursor-cloud-agents/tools/status.ts +39 -0
  226. package/src/extensions.ts +98 -0
  227. package/src/index.ts +2 -0
  228. package/src/internal/advertise-tools.ts +37 -12
  229. package/src/internal/as-of.ts +163 -0
  230. package/src/internal/cli-deploy.ts +86 -23
  231. package/src/internal/continuation-identity.ts +3 -1
  232. package/src/internal/cursor/hosted-deployment-secrets.ts +72 -0
  233. package/src/internal/cursor-deployment-relay.ts +65 -8
  234. package/src/internal/deploy-client.ts +73 -18
  235. package/src/internal/discovery.ts +470 -113
  236. package/src/internal/distribution.ts +5 -0
  237. package/src/internal/framework-file-storage.ts +39 -5
  238. package/src/internal/hosted-delivery.ts +38 -63
  239. package/src/internal/hosted-execution-diag.ts +6 -0
  240. package/src/internal/hosted-execution-flush.ts +142 -0
  241. package/src/internal/mcp-endpoint.ts +1 -0
  242. package/src/internal/mcp-host.ts +40 -14
  243. package/src/internal/review-comments.ts +5 -2
  244. package/src/internal/server.ts +81 -100
  245. package/src/internal/session-engine.ts +56 -28
  246. package/src/internal/session-run-log.ts +2 -1
  247. package/src/internal/start-log-store.ts +177 -0
  248. package/src/internal/tool-policy.ts +39 -9
  249. package/src/storage-protocol.ts +37 -0
  250. package/src/storage.ts +1 -0
  251. package/src/types.ts +172 -14
  252. package/dist/docs/assets/chunks/@localSearchIndexroot.j70vvPL4.js +0 -1
  253. package/dist/playground/assets/index-BszoQDc6.css +0 -1
  254. /package/dist/docs/assets/{reference_http-api.md.BEJx9XVj.lean.js → reference_http-api.md.oySXBO8o.lean.js} +0 -0
@@ -329,6 +329,14 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
329
329
  if (manifests.every((manifest) => manifest.architecture === "v2")) {
330
330
  return deployAgentsdkApplication(ctx, options, targets, {
331
331
  hasScmChannels: manifests.some((manifest) => manifest.hasScmChannels),
332
+ declaredSecretNames: [
333
+ ...new Set(
334
+ manifests.flatMap((manifest) => manifest.hosting?.secretNames ?? [])
335
+ ),
336
+ ],
337
+ hasDeploymentsChannels: manifests.some(
338
+ (manifest) => manifest.hasDeploymentsChannels
339
+ ),
332
340
  });
333
341
  }
334
342
  if (
@@ -741,6 +749,8 @@ async function deployOne(
741
749
  agentPath,
742
750
  repositories: options.cursorEventsRepos,
743
751
  hasScmChannels: manifest.hasScmChannels,
752
+ declaredSecretNames: hosting?.secretNames,
753
+ hasDeploymentsChannels: manifest.hasDeploymentsChannels,
744
754
  multiTenant: options.multiTenant === true,
745
755
  });
746
756
  }
@@ -871,7 +881,11 @@ async function deployAgentsdkApplication(
871
881
  ctx: DeployCliContext,
872
882
  options: DeployCliOptions,
873
883
  targets: readonly DeployTarget[],
874
- declarations: { hasScmChannels: boolean }
884
+ declarations: {
885
+ hasScmChannels: boolean;
886
+ declaredSecretNames: string[];
887
+ hasDeploymentsChannels: boolean;
888
+ }
875
889
  ): Promise<number> {
876
890
  const parentDir = resolve(options.dir ?? ".");
877
891
  const slug = slugFromDirectoryName(basename(parentDir));
@@ -936,16 +950,7 @@ async function deployAgentsdkApplication(
936
950
  );
937
951
  }
938
952
 
939
- // Components bake sequentially on the control plane, so the default wait
940
- // budget is per component; otherwise a 2+ component deploy routinely hits
941
- // the client-side timeout while the server-side deploy is still fine. An
942
- // explicitly passed timeout always wins.
943
- const waitCtx =
944
- options.timeoutMs === undefined
945
- ? { ...ctx, timeoutMs: ctx.timeoutMs * targets.length }
946
- : ctx;
947
-
948
- return deployOneAgentsdk(waitCtx, undefined, {
953
+ return deployOneAgentsdk(ctx, undefined, {
949
954
  slug,
950
955
  gitRepoUrl,
951
956
  gitRef,
@@ -953,6 +958,8 @@ async function deployAgentsdkApplication(
953
958
  components,
954
959
  repositories: options.cursorEventsRepos,
955
960
  hasScmChannels: declarations.hasScmChannels,
961
+ declaredSecretNames: declarations.declaredSecretNames,
962
+ hasDeploymentsChannels: declarations.hasDeploymentsChannels,
956
963
  multiTenant: options.multiTenant === true,
957
964
  });
958
965
  }
@@ -974,6 +981,10 @@ async function deployOneAgentsdk(
974
981
  repositories: string[] | undefined;
975
982
  /** Any component declares a GitHub/Origin channel. */
976
983
  hasScmChannels: boolean;
984
+ /** Union of application component secret declarations. */
985
+ declaredSecretNames?: string[];
986
+ /** Any component declares a deployments channel (same repo scope). */
987
+ hasDeploymentsChannels?: boolean;
977
988
  /** Redeploy an existing Cursor-managed product. */
978
989
  multiTenant: boolean;
979
990
  }
@@ -1048,6 +1059,9 @@ async function deployOneAgentsdk(
1048
1059
  );
1049
1060
  return 1;
1050
1061
  }
1062
+ warnMissingSecrets(ctx, slug, args.declaredSecretNames, detail.secretNames);
1063
+ const watchesRepositories =
1064
+ args.hasScmChannels || args.hasDeploymentsChannels === true;
1051
1065
  ctx.out(
1052
1066
  `Deployed ${slug} (team ${ctx.teamId})\n` +
1053
1067
  ` status: running\n` +
@@ -1059,15 +1073,16 @@ async function deployOneAgentsdk(
1059
1073
  : ` components: ${args.components
1060
1074
  .map((component) => component.componentKey)
1061
1075
  .join(", ")}\n`) +
1062
- (detail.repositories === undefined || !args.hasScmChannels
1076
+ (detail.repositories === undefined || !watchesRepositories
1063
1077
  ? ""
1064
1078
  : ` watching: ${detail.repositories.length === 0 ? "(nothing)" : detail.repositories.join(", ")}\n`)
1065
1079
  );
1066
- // An agent with GitHub/Origin channels bound to no repository scope gets
1067
- // no deliveries at all, which is otherwise invisible in this output.
1068
- if (args.hasScmChannels && detail.repositories?.length === 0) {
1080
+ // An agent with GitHub/Origin/deployments channels bound to no repository
1081
+ // scope gets no deliveries at all (deployments channels receive the
1082
+ // watched repos' deployment events), which is otherwise invisible here.
1083
+ if (watchesRepositories && detail.repositories?.length === 0) {
1069
1084
  ctx.err(
1070
- `warning: ${slug} watches no repositories, so its GitHub/Origin channels receive no events.\n` +
1085
+ `warning: ${slug} watches no repositories, so its GitHub/Origin/deployments channels receive no events.\n` +
1071
1086
  ` Redeploy with --cursor-events-repo <owner>/<name> to set the scope.\n`
1072
1087
  );
1073
1088
  }
@@ -1185,6 +1200,12 @@ interface DeployManifest {
1185
1200
  * while the deployment is bound to at least one `owner/name` scope.
1186
1201
  */
1187
1202
  hasScmChannels: boolean;
1203
+ /**
1204
+ * The agent has deployments channels; deploy-source edges derive from
1205
+ * the same watched-repo set as GitHub/Origin, so it too receives
1206
+ * nothing while the deployment watches no repositories.
1207
+ */
1208
+ hasDeploymentsChannels: boolean;
1188
1209
  }
1189
1210
 
1190
1211
  /**
@@ -1224,6 +1245,9 @@ async function readDeployManifest(dir: string): Promise<DeployManifest> {
1224
1245
  const kind = channel.definition.meta?.kind;
1225
1246
  return kind === "github" || kind === "origin";
1226
1247
  }),
1248
+ hasDeploymentsChannels: project.channels.some(
1249
+ (channel) => channel.definition.meta?.kind === "deployments"
1250
+ ),
1227
1251
  };
1228
1252
  }
1229
1253
 
@@ -1450,6 +1474,9 @@ function renderAgentkitDeploymentStatus(
1450
1474
  }`
1451
1475
  );
1452
1476
  }
1477
+ if (detail.secretNames !== undefined && detail.secretNames.length > 0) {
1478
+ lines.push(` secrets: ${detail.secretNames.join(", ")}`);
1479
+ }
1453
1480
  if (detail.lastError !== null && detail.lastError !== undefined) {
1454
1481
  lines.push(` last error: ${sanitizeCustomerError(detail.lastError)}`);
1455
1482
  }
@@ -1650,6 +1677,21 @@ export async function cmdRotatePodCredential(
1650
1677
  // secrets set|list|unset
1651
1678
  // ============================================================================
1652
1679
 
1680
+ async function secretDeploymentArchitecture(
1681
+ ctx: DeployCliContext,
1682
+ slug: string
1683
+ ): Promise<"v1" | "v2"> {
1684
+ try {
1685
+ await ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug });
1686
+ return "v2";
1687
+ } catch (error) {
1688
+ if (error instanceof DeployApiError && error.status === 404) {
1689
+ return "v1";
1690
+ }
1691
+ throw error;
1692
+ }
1693
+ }
1694
+
1653
1695
  export async function cmdSecretsSet(
1654
1696
  slug: string | undefined,
1655
1697
  specs: string[],
@@ -1712,11 +1754,19 @@ export async function cmdSecretsSet(
1712
1754
 
1713
1755
  let secretNames: string[];
1714
1756
  try {
1715
- secretNames = await ctx.client.setSecrets({
1716
- teamId: ctx.teamId,
1717
- slug,
1718
- secrets,
1719
- });
1757
+ const architecture = await secretDeploymentArchitecture(ctx, slug);
1758
+ secretNames =
1759
+ architecture === "v2"
1760
+ ? await ctx.client.setAgentkitSecrets({
1761
+ teamId: ctx.teamId,
1762
+ slug,
1763
+ secrets,
1764
+ })
1765
+ : await ctx.client.setSecrets({
1766
+ teamId: ctx.teamId,
1767
+ slug,
1768
+ secrets,
1769
+ });
1720
1770
  } catch (error) {
1721
1771
  return renderApiError(error, ctx.err);
1722
1772
  }
@@ -1748,7 +1798,11 @@ export async function cmdSecretsList(
1748
1798
  }
1749
1799
  let secrets: SecretInfo[];
1750
1800
  try {
1751
- secrets = await ctx.client.listSecrets({ teamId: ctx.teamId, slug });
1801
+ const architecture = await secretDeploymentArchitecture(ctx, slug);
1802
+ secrets =
1803
+ architecture === "v2"
1804
+ ? await ctx.client.listAgentkitSecrets({ teamId: ctx.teamId, slug })
1805
+ : await ctx.client.listSecrets({ teamId: ctx.teamId, slug });
1752
1806
  } catch (error) {
1753
1807
  return renderApiError(error, ctx.err);
1754
1808
  }
@@ -1787,7 +1841,16 @@ export async function cmdSecretsUnset(
1787
1841
  return ctx;
1788
1842
  }
1789
1843
  try {
1790
- await ctx.client.deleteSecret({ teamId: ctx.teamId, slug, name });
1844
+ const architecture = await secretDeploymentArchitecture(ctx, slug);
1845
+ if (architecture === "v2") {
1846
+ await ctx.client.deleteAgentkitSecret({
1847
+ teamId: ctx.teamId,
1848
+ slug,
1849
+ name,
1850
+ });
1851
+ } else {
1852
+ await ctx.client.deleteSecret({ teamId: ctx.teamId, slug, name });
1853
+ }
1791
1854
  } catch (error) {
1792
1855
  if (error instanceof DeployApiError && error.status === 404) {
1793
1856
  ctx.err(
@@ -119,7 +119,9 @@ export const BUILTINS: readonly ContinuationBuiltin[] = [
119
119
  {
120
120
  type: "deployment",
121
121
  kind: "deployments",
122
- events: ["deployment.created", "deployment.succeeded"],
122
+ // All three factory stream events carry `deploymentId`; keep in
123
+ // lockstep with the control plane's `conversation/deployments.ts`.
124
+ events: ["deployment.created", "deployment.succeeded", "deployment.failed"],
123
125
  format: "deployments:{deploymentId}",
124
126
  },
125
127
  ] as const;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Task-time application secrets for hosted v2 freeze restores.
3
+ *
4
+ * The deployment host rewrites this tmpfs document before each admission and
5
+ * execution request. July reloads it synchronously because the process was
6
+ * already running in the frozen snapshot when the task-specific values
7
+ * arrived.
8
+ */
9
+
10
+ import { readFileSync } from "node:fs";
11
+
12
+ export const DEFAULT_AGENT_SERVE_DEPLOYMENT_SECRETS_FILE =
13
+ "/run/cursor/secrets/AGENT_SERVE_DEPLOYMENT_SECRETS.json";
14
+
15
+ const SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]{0,63}$/;
16
+ const appliedSecretNames = new Set<string>();
17
+
18
+ function parseBoundSecrets(path: string): Record<string, string> | undefined {
19
+ let parsed: unknown;
20
+ try {
21
+ parsed = JSON.parse(readFileSync(path, "utf8"));
22
+ } catch {
23
+ return undefined;
24
+ }
25
+ if (parsed === null || typeof parsed !== "object") {
26
+ return undefined;
27
+ }
28
+ const envelope = parsed as { v?: unknown; secrets?: unknown };
29
+ if (
30
+ envelope.v !== 1 ||
31
+ envelope.secrets === null ||
32
+ typeof envelope.secrets !== "object" ||
33
+ Array.isArray(envelope.secrets)
34
+ ) {
35
+ return undefined;
36
+ }
37
+ const secrets: Record<string, string> = {};
38
+ for (const [name, value] of Object.entries(envelope.secrets)) {
39
+ if (
40
+ SECRET_NAME_PATTERN.test(name) &&
41
+ !name.startsWith("CURSOR_") &&
42
+ typeof value === "string"
43
+ ) {
44
+ secrets[name] = value;
45
+ }
46
+ }
47
+ return secrets;
48
+ }
49
+
50
+ /**
51
+ * Refresh `process.env` from the latest bound document. A missing or malformed
52
+ * file is a no-op; an empty valid document removes values loaded by an earlier
53
+ * bind after the deployment secret was unset.
54
+ */
55
+ export function refreshBoundDeploymentSecrets(
56
+ path: string = DEFAULT_AGENT_SERVE_DEPLOYMENT_SECRETS_FILE
57
+ ): void {
58
+ const secrets = parseBoundSecrets(path);
59
+ if (secrets === undefined) {
60
+ return;
61
+ }
62
+ for (const name of appliedSecretNames) {
63
+ if (!(name in secrets)) {
64
+ delete process.env[name];
65
+ }
66
+ }
67
+ appliedSecretNames.clear();
68
+ for (const [name, value] of Object.entries(secrets)) {
69
+ process.env[name] = value;
70
+ appliedSecretNames.add(name);
71
+ }
72
+ }
@@ -33,17 +33,33 @@ interface SubscriptionStatus {
33
33
  detail?: string;
34
34
  }
35
35
 
36
+ export type DeploymentSubscriptionSource =
37
+ | readonly ResolvedSubscription[]
38
+ | (() => readonly ResolvedSubscription[]);
39
+
36
40
  export interface DeploymentEventRelayOptions {
37
41
  baseUrl: string;
38
42
  apiKey: string;
39
43
  stateDir: string;
40
- subscriptions: readonly ResolvedSubscription[];
44
+ /**
45
+ * Snapshot or a getter. A getter is re-read on every registration
46
+ * heartbeat so a channel that refreshes `deploySourceUris` after start
47
+ * can add sources without restarting the relay.
48
+ */
49
+ subscriptions: DeploymentSubscriptionSource;
41
50
  dispatch: (event: DeploymentEvent) => Promise<void>;
42
51
  logger: (line: string) => void;
43
52
  fetchImpl?: typeof fetch;
44
53
  pollWaitMs?: number;
45
54
  }
46
55
 
56
+ /** Current subscriptions: a live getter, otherwise the snapshot. */
57
+ export function currentDeploymentSubscriptions(
58
+ subscriptions: DeploymentSubscriptionSource
59
+ ): readonly ResolvedSubscription[] {
60
+ return typeof subscriptions === "function" ? subscriptions() : subscriptions;
61
+ }
62
+
47
63
  /** Normalize the channel's `deploySourceUris` option into concrete subscriptions. */
48
64
  export function resolveDeploymentSubscriptions(options: {
49
65
  deploySourceUris: readonly (string | DeploymentSubscription)[];
@@ -90,10 +106,46 @@ export function resolveDeploymentSubscriptions(options: {
90
106
  ? []
91
107
  : [...new Set([...existing.environments, ...environments])];
92
108
  }
93
- // Empty is legal here and means "this channel wants nothing", which is how
94
- // an env-configured channel stays inert without a deploy-source list. The
95
- // wire-level empty list means deregister, so the host simply never
96
- // registers it.
109
+ // Empty is legal here and means "this channel wants nothing". The
110
+ // wire-level empty list means deregister. The host re-resolves on each
111
+ // heartbeat, so a channel that later fills `deploySourceUris` can
112
+ // register without a restart.
113
+ return [...bySource.values()];
114
+ }
115
+
116
+ /**
117
+ * Union per-channel subscriptions, widening events and environments when
118
+ * the same deploy source appears more than once. An empty environment
119
+ * list means every environment and absorbs a narrower list.
120
+ */
121
+ export function mergeResolvedSubscriptions(
122
+ groups: readonly (readonly ResolvedSubscription[])[]
123
+ ): ResolvedSubscription[] {
124
+ const bySource = new Map<string, ResolvedSubscription>();
125
+ for (const group of groups) {
126
+ for (const subscription of group) {
127
+ const existing = bySource.get(subscription.deploySourceUri);
128
+ if (existing === undefined) {
129
+ bySource.set(subscription.deploySourceUri, {
130
+ deploySourceUri: subscription.deploySourceUri,
131
+ environments: [...subscription.environments],
132
+ events: new Set(subscription.events),
133
+ });
134
+ continue;
135
+ }
136
+ for (const event of subscription.events) existing.events.add(event);
137
+ existing.environments =
138
+ existing.environments.length === 0 ||
139
+ subscription.environments.length === 0
140
+ ? []
141
+ : [
142
+ ...new Set([
143
+ ...existing.environments,
144
+ ...subscription.environments,
145
+ ]),
146
+ ];
147
+ }
148
+ }
97
149
  return [...bySource.values()];
98
150
  }
99
151
 
@@ -176,7 +228,9 @@ export class DeploymentEventRelay {
176
228
  pollPath: "/v0/deployment-events",
177
229
  buildRegistrationBody: (consumerId) => ({
178
230
  consumerId,
179
- subscriptions: options.subscriptions.map((subscription) => ({
231
+ subscriptions: currentDeploymentSubscriptions(
232
+ options.subscriptions
233
+ ).map((subscription) => ({
180
234
  deploySourceUri: subscription.deploySourceUri,
181
235
  environments: subscription.environments,
182
236
  events: [...subscription.events],
@@ -190,12 +244,15 @@ export class DeploymentEventRelay {
190
244
  }),
191
245
  applyRegistrationResponse: (json) => {
192
246
  const result = json as { deploySourceUris?: SubscriptionStatus[] };
247
+ const wantedSubscriptions = currentDeploymentSubscriptions(
248
+ options.subscriptions
249
+ );
193
250
  this.subscribed = new Map();
194
251
  this.loggedSkippedSources.clear();
195
252
  const lines: string[] = [];
196
253
  const confirmed: string[] = [];
197
254
  for (const status of result.deploySourceUris ?? []) {
198
- const wanted = options.subscriptions.find(
255
+ const wanted = wantedSubscriptions.find(
199
256
  (subscription) =>
200
257
  subscription.deploySourceUri === status.deploySourceUri
201
258
  );
@@ -210,7 +267,7 @@ export class DeploymentEventRelay {
210
267
  }
211
268
  if (confirmed.length > 0) {
212
269
  const kinds = [
213
- ...new Set(options.subscriptions.flatMap((s) => [...s.events])),
270
+ ...new Set(wantedSubscriptions.flatMap((s) => [...s.events])),
214
271
  ];
215
272
  lines.push(
216
273
  `subscribed to ${confirmed.join(", ")} for events: ${kinds.join(", ")}`
@@ -267,6 +267,8 @@ export interface AgentkitDeployStatus {
267
267
  * Absent on control planes that predate the field.
268
268
  */
269
269
  repositories?: string[];
270
+ /** Names of application-scoped secrets (values are never returned). */
271
+ secretNames?: string[];
270
272
  }
271
273
 
272
274
  export const TERMINAL_AGENTKIT_DEPLOY_STATUSES: ReadonlySet<AgentkitCustomerStatus> =
@@ -359,6 +361,7 @@ const agentkitDeployStatusSchema = z
359
361
  statusMessage: z.string().nullable(),
360
362
  // Optional: backends that predate the v2 bake-failure column omit it.
361
363
  lastError: z.string().nullable().optional(),
364
+ secretNames: z.array(z.string()).optional(),
362
365
  })
363
366
  .passthrough();
364
367
 
@@ -740,24 +743,7 @@ export class AgentServeDeployClient {
740
743
  slug: string;
741
744
  secrets: Array<{ name: string; value: string }>;
742
745
  }): Promise<string[]> {
743
- for (const { name, value } of args.secrets) {
744
- const nameError = validateSettableSecretName(name);
745
- if (nameError !== undefined) {
746
- throw new DeployApiError(nameError, 400);
747
- }
748
- if (Buffer.byteLength(value, "utf8") > MAX_SECRET_VALUE_BYTES) {
749
- throw new DeployApiError(
750
- `The value for ${name} is too large (max ${MAX_SECRET_VALUE_BYTES} bytes).`,
751
- 400
752
- );
753
- }
754
- }
755
- if (args.secrets.length > MAX_SECRETS) {
756
- throw new DeployApiError(
757
- `Too many secrets in one request (${args.secrets.length}); deployments hold at most ${MAX_SECRETS}.`,
758
- 400
759
- );
760
- }
746
+ this.validateSecrets(args.secrets);
761
747
  const raw = await this.request({
762
748
  method: "PUT",
763
749
  path: `/internal/agent-serve/deployments/${encodeURIComponent(args.slug)}/secrets`,
@@ -767,6 +753,22 @@ export class AgentServeDeployClient {
767
753
  return setSecretsResponseSchema.parse(raw).secretNames;
768
754
  }
769
755
 
756
+ /** Upsert application-scoped secrets on an AgentSDK v2 deployment. */
757
+ async setAgentkitSecrets(args: {
758
+ teamId: number;
759
+ slug: string;
760
+ secrets: Array<{ name: string; value: string }>;
761
+ }): Promise<string[]> {
762
+ this.validateSecrets(args.secrets);
763
+ const raw = await this.request({
764
+ method: "PUT",
765
+ path: `/internal/agentsdk/deployments/${encodeURIComponent(args.slug)}/secrets`,
766
+ body: { teamId: args.teamId, secrets: args.secrets },
767
+ context: { verb: "set secrets", slug: args.slug },
768
+ });
769
+ return setSecretsResponseSchema.parse(raw).secretNames;
770
+ }
771
+
770
772
  /** Secret names + creation times; values are never returned. */
771
773
  async listSecrets(args: {
772
774
  teamId: number;
@@ -780,6 +782,19 @@ export class AgentServeDeployClient {
780
782
  return listSecretsResponseSchema.parse(raw).secrets;
781
783
  }
782
784
 
785
+ /** Secret names + creation times for an AgentSDK v2 application. */
786
+ async listAgentkitSecrets(args: {
787
+ teamId: number;
788
+ slug: string;
789
+ }): Promise<SecretInfo[]> {
790
+ const raw = await this.request({
791
+ method: "GET",
792
+ path: `/internal/agentsdk/deployments/${encodeURIComponent(args.slug)}/secrets?teamId=${args.teamId}`,
793
+ context: { verb: "list secrets", slug: args.slug },
794
+ });
795
+ return listSecretsResponseSchema.parse(raw).secrets;
796
+ }
797
+
783
798
  /** Remove one secret by name; 404 when it is not set. */
784
799
  async deleteSecret(args: {
785
800
  teamId: number;
@@ -797,6 +812,46 @@ export class AgentServeDeployClient {
797
812
  });
798
813
  }
799
814
 
815
+ /** Remove one application-scoped secret from an AgentSDK v2 deployment. */
816
+ async deleteAgentkitSecret(args: {
817
+ teamId: number;
818
+ slug: string;
819
+ name: string;
820
+ }): Promise<void> {
821
+ const nameError = validateSettableSecretName(args.name);
822
+ if (nameError !== undefined) {
823
+ throw new DeployApiError(nameError, 400);
824
+ }
825
+ await this.request({
826
+ method: "DELETE",
827
+ path: `/internal/agentsdk/deployments/${encodeURIComponent(args.slug)}/secrets/${encodeURIComponent(args.name)}?teamId=${args.teamId}`,
828
+ context: { verb: "unset secret", slug: args.slug },
829
+ });
830
+ }
831
+
832
+ private validateSecrets(
833
+ secrets: Array<{ name: string; value: string }>
834
+ ): void {
835
+ for (const { name, value } of secrets) {
836
+ const nameError = validateSettableSecretName(name);
837
+ if (nameError !== undefined) {
838
+ throw new DeployApiError(nameError, 400);
839
+ }
840
+ if (Buffer.byteLength(value, "utf8") > MAX_SECRET_VALUE_BYTES) {
841
+ throw new DeployApiError(
842
+ `The value for ${name} is too large (max ${MAX_SECRET_VALUE_BYTES} bytes).`,
843
+ 400
844
+ );
845
+ }
846
+ }
847
+ if (secrets.length > MAX_SECRETS) {
848
+ throw new DeployApiError(
849
+ `Too many secrets in one request (${secrets.length}); deployments hold at most ${MAX_SECRETS}.`,
850
+ 400
851
+ );
852
+ }
853
+ }
854
+
800
855
  /**
801
856
  * Rotate the deployment's pod credential. Returns the masked new key —
802
857
  * the raw key is injected into the pod on the next deploy, never