@cursor/july 0.1.39 → 0.1.41

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 (259) hide show
  1. package/AGENTS.md +4 -3
  2. package/dist/bin/agent-serve.js +28 -9
  3. package/dist/channels/slack/api.d.ts.map +1 -1
  4. package/dist/channels/slack/api.js +18 -8
  5. package/dist/channels/slack/attachments.d.ts +82 -0
  6. package/dist/channels/slack/attachments.d.ts.map +1 -0
  7. package/dist/channels/slack/attachments.js +520 -0
  8. package/dist/channels/slack/cursor-account.d.ts +7 -1
  9. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  10. package/dist/channels/slack/cursor-account.js +2 -2
  11. package/dist/channels/slack/defaults.d.ts.map +1 -1
  12. package/dist/channels/slack/defaults.js +26 -17
  13. package/dist/channels/slack/dispatch.d.ts +4 -3
  14. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  15. package/dist/channels/slack/dispatch.js +84 -21
  16. package/dist/channels/slack/inbound.d.ts +6 -0
  17. package/dist/channels/slack/inbound.d.ts.map +1 -1
  18. package/dist/channels/slack/inbound.js +48 -2
  19. package/dist/channels/slack/index.d.ts +1 -0
  20. package/dist/channels/slack/index.d.ts.map +1 -1
  21. package/dist/channels/slack/index.js +1 -0
  22. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  23. package/dist/channels/slack/live-delivery.js +16 -4
  24. package/dist/channels/slack/manifest.d.ts.map +1 -1
  25. package/dist/channels/slack/manifest.js +1 -0
  26. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  27. package/dist/channels/slack/slack-channel.js +7 -4
  28. package/dist/channels/slack/types.d.ts +49 -1
  29. package/dist/channels/slack/types.d.ts.map +1 -1
  30. package/dist/docs/404.html +2 -2
  31. package/dist/docs/ab.html +6 -6
  32. package/dist/docs/assets/{ab.md.6cLOW7--.js → ab.md.hdxsvX5M.js} +2 -2
  33. package/dist/docs/assets/{app.BQ8Hihdf.js → app.mIw24pb1.js} +1 -1
  34. package/dist/docs/assets/{building-with-agents.md.txrcGU2B.js → building-with-agents.md.PeZaZA1P.js} +1 -1
  35. package/dist/docs/assets/chunks/@localSearchIndexroot.BA7V8dEJ.js +1 -0
  36. package/dist/docs/assets/chunks/{VPLocalSearchBox.BBCr8Yuy.js → VPLocalSearchBox.hOzpPwrG.js} +1 -1
  37. package/dist/docs/assets/chunks/{theme.CK_NiGC-.js → theme.BtFhUYmh.js} +2 -2
  38. package/dist/docs/assets/{deployment.md.CuK5SNjN.js → deployment.md.BHVjVqRa.js} +4 -4
  39. package/dist/docs/assets/{deployment.md.CuK5SNjN.lean.js → deployment.md.BHVjVqRa.lean.js} +1 -1
  40. package/dist/docs/assets/{evals.md.BQXI3rXy.js → evals.md.qeoGgPw0.js} +3 -3
  41. package/dist/docs/assets/example-agents_index.md.xmJ87d_3.js +2 -0
  42. package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.js → guides_webhooks.md.DB-r_er9.js} +47 -4
  43. package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.lean.js → guides_webhooks.md.DB-r_er9.lean.js} +1 -1
  44. package/dist/docs/assets/index.md.BKpbkCa6.js +24 -0
  45. package/dist/docs/assets/{index.md.CmhptOmN.lean.js → index.md.BKpbkCa6.lean.js} +1 -1
  46. package/dist/docs/assets/{quickstart.md.C_b6ESpD.js → quickstart.md.rIbfahJM.js} +4 -4
  47. package/dist/docs/assets/{reference_cli.md.Byvrg8eu.js → reference_cli.md.DqEqQpGn.js} +5 -5
  48. package/dist/docs/assets/{reference_cli.md.Byvrg8eu.lean.js → reference_cli.md.DqEqQpGn.lean.js} +1 -1
  49. package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.js → reference_hooks.md.BHeG-vjK.js} +2 -2
  50. package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.lean.js → reference_hooks.md.BHeG-vjK.lean.js} +1 -1
  51. package/dist/docs/assets/{storage.md.mQDtIULc.js → storage.md.DcGSnIxX.js} +10 -4
  52. package/dist/docs/assets/{storage.md.mQDtIULc.lean.js → storage.md.DcGSnIxX.lean.js} +1 -1
  53. package/dist/docs/assets/troubleshooting.md.CkgwmVI0.js +1 -0
  54. package/dist/docs/assets/{troubleshooting.md.B5RVX_tL.lean.js → troubleshooting.md.CkgwmVI0.lean.js} +1 -1
  55. package/dist/docs/building-with-agents.html +5 -5
  56. package/dist/docs/concepts.html +3 -3
  57. package/dist/docs/deployment.html +6 -6
  58. package/dist/docs/evals.html +7 -7
  59. package/dist/docs/example-agents/approval-buddy.html +3 -3
  60. package/dist/docs/example-agents/benny.html +3 -3
  61. package/dist/docs/example-agents/bugbot.html +3 -3
  62. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  63. package/dist/docs/example-agents/codeowners-review.html +3 -3
  64. package/dist/docs/example-agents/concierge.html +3 -3
  65. package/dist/docs/example-agents/fsd.html +3 -3
  66. package/dist/docs/example-agents/index.html +5 -5
  67. package/dist/docs/example-agents/knowledge-base.html +3 -3
  68. package/dist/docs/example-agents/oncall.html +3 -3
  69. package/dist/docs/example-agents/security-reviewer.html +3 -3
  70. package/dist/docs/example-agents/slack-agent.html +3 -3
  71. package/dist/docs/example-agents/weather-agent.html +3 -3
  72. package/dist/docs/guides/agent-to-agent.html +3 -3
  73. package/dist/docs/guides/cloud-runtime.html +3 -3
  74. package/dist/docs/guides/github.html +3 -3
  75. package/dist/docs/guides/human-in-the-loop.html +3 -3
  76. package/dist/docs/guides/mcp-oauth.html +3 -3
  77. package/dist/docs/guides/slack.html +3 -3
  78. package/dist/docs/guides/webhooks.html +49 -6
  79. package/dist/docs/hashmap.json +1 -1
  80. package/dist/docs/hillclimbing.html +3 -3
  81. package/dist/docs/index.html +7 -7
  82. package/dist/docs/quickstart.html +8 -8
  83. package/dist/docs/reference/agent-config.html +3 -3
  84. package/dist/docs/reference/artifacts.html +3 -3
  85. package/dist/docs/reference/channels.html +3 -3
  86. package/dist/docs/reference/cli.html +8 -8
  87. package/dist/docs/reference/connections.html +3 -3
  88. package/dist/docs/reference/hooks.html +5 -5
  89. package/dist/docs/reference/http-api.html +3 -3
  90. package/dist/docs/reference/instructions.html +3 -3
  91. package/dist/docs/reference/playground.html +3 -3
  92. package/dist/docs/reference/project-layout.html +3 -3
  93. package/dist/docs/reference/prompt.html +3 -3
  94. package/dist/docs/reference/schedules.html +3 -3
  95. package/dist/docs/reference/sessions.html +3 -3
  96. package/dist/docs/reference/skills.html +3 -3
  97. package/dist/docs/reference/subagents.html +3 -3
  98. package/dist/docs/reference/tools.html +3 -3
  99. package/dist/docs/scaffolding-agents.html +3 -3
  100. package/dist/docs/storage.html +14 -8
  101. package/dist/docs/troubleshooting.html +5 -5
  102. package/dist/index.d.ts +4 -2
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +3 -1
  105. package/dist/internal/cli-ax.d.ts.map +1 -1
  106. package/dist/internal/cli-ax.js +22 -2
  107. package/dist/internal/cli-deploy.d.ts +6 -0
  108. package/dist/internal/cli-deploy.d.ts.map +1 -1
  109. package/dist/internal/cli-deploy.js +14 -4
  110. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  111. package/dist/internal/cli-mcp-oauth.js +3 -1
  112. package/dist/internal/cli-mcp.js +4 -4
  113. package/dist/internal/cursor-event-relay.d.ts +45 -0
  114. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  115. package/dist/internal/cursor-event-relay.js +96 -2
  116. package/dist/internal/deploy-client.js +1 -1
  117. package/dist/internal/discovery.d.ts.map +1 -1
  118. package/dist/internal/discovery.js +35 -7
  119. package/dist/internal/distribution.d.ts.map +1 -1
  120. package/dist/internal/distribution.js +3 -1
  121. package/dist/internal/durable-session-store.d.ts +48 -0
  122. package/dist/internal/durable-session-store.d.ts.map +1 -0
  123. package/dist/internal/durable-session-store.js +86 -0
  124. package/dist/internal/hosting.d.ts +1 -1
  125. package/dist/internal/hosting.js +2 -2
  126. package/dist/internal/init-project.d.ts +34 -0
  127. package/dist/internal/init-project.d.ts.map +1 -1
  128. package/dist/internal/init-project.js +121 -2
  129. package/dist/internal/ledger-serving.d.ts +101 -0
  130. package/dist/internal/ledger-serving.d.ts.map +1 -0
  131. package/dist/internal/ledger-serving.js +174 -0
  132. package/dist/internal/listen-port.d.ts +22 -0
  133. package/dist/internal/listen-port.d.ts.map +1 -0
  134. package/dist/internal/listen-port.js +77 -0
  135. package/dist/internal/mcp-oauth.d.ts +12 -1
  136. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  137. package/dist/internal/mcp-oauth.js +25 -3
  138. package/dist/internal/playground/static.d.ts +21 -4
  139. package/dist/internal/playground/static.d.ts.map +1 -1
  140. package/dist/internal/playground/static.js +82 -18
  141. package/dist/internal/sdk-runner.d.ts.map +1 -1
  142. package/dist/internal/sdk-runner.js +10 -1
  143. package/dist/internal/server.d.ts.map +1 -1
  144. package/dist/internal/server.js +67 -49
  145. package/dist/internal/session-engine.d.ts +57 -1
  146. package/dist/internal/session-engine.d.ts.map +1 -1
  147. package/dist/internal/session-engine.js +117 -28
  148. package/dist/internal/slack-provision-client.js +1 -1
  149. package/dist/internal/workspace.d.ts +6 -2
  150. package/dist/internal/workspace.d.ts.map +1 -1
  151. package/dist/internal/workspace.js +10 -2
  152. package/dist/kv/memory-kv.d.ts +23 -0
  153. package/dist/kv/memory-kv.d.ts.map +1 -0
  154. package/dist/kv/memory-kv.js +103 -0
  155. package/dist/kv.d.ts +22 -0
  156. package/dist/kv.d.ts.map +1 -0
  157. package/dist/kv.js +20 -0
  158. package/dist/ledger/session-ledger.d.ts +204 -0
  159. package/dist/ledger/session-ledger.d.ts.map +1 -0
  160. package/dist/ledger/session-ledger.js +382 -0
  161. package/dist/ledger.d.ts +11 -0
  162. package/dist/ledger.d.ts.map +1 -0
  163. package/dist/ledger.js +9 -0
  164. package/dist/playground/assets/index-B3HBneZZ.js +85 -0
  165. package/dist/playground/assets/index-Bqk9nplh.css +1 -0
  166. package/dist/playground/index.html +2 -2
  167. package/dist/storage-backends/cursor-hosted.d.ts +25 -3
  168. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  169. package/dist/storage-backends/cursor-hosted.js +50 -2
  170. package/dist/storage-backends/file-kv.d.ts +5 -2
  171. package/dist/storage-backends/file-kv.d.ts.map +1 -1
  172. package/dist/storage-backends/file-kv.js +107 -7
  173. package/dist/storage.d.ts +60 -5
  174. package/dist/storage.d.ts.map +1 -1
  175. package/dist/storage.js +28 -4
  176. package/dist/types.d.ts +123 -3
  177. package/dist/types.d.ts.map +1 -1
  178. package/dist/types.js +14 -0
  179. package/docs/README.md +10 -7
  180. package/docs/ab.md +2 -2
  181. package/docs/building-with-agents.md +1 -1
  182. package/docs/deployment.md +34 -11
  183. package/docs/evals.md +3 -3
  184. package/docs/example-agents/index.md +1 -1
  185. package/docs/guides/webhooks.md +87 -0
  186. package/docs/quickstart.md +7 -8
  187. package/docs/reference/cli.md +15 -6
  188. package/docs/reference/hooks.md +8 -2
  189. package/docs/storage.md +67 -7
  190. package/docs/troubleshooting.md +10 -2
  191. package/package.json +20 -7
  192. package/skills/create-agent/SKILL.md +1 -1
  193. package/src/bin/agent-serve.ts +27 -11
  194. package/src/channels/slack/api.ts +19 -14
  195. package/src/channels/slack/attachments.ts +635 -0
  196. package/src/channels/slack/cursor-account.ts +8 -0
  197. package/src/channels/slack/defaults.ts +34 -18
  198. package/src/channels/slack/dispatch.ts +121 -6
  199. package/src/channels/slack/inbound.ts +60 -0
  200. package/src/channels/slack/index.ts +1 -0
  201. package/src/channels/slack/live-delivery.ts +17 -3
  202. package/src/channels/slack/manifest.ts +1 -0
  203. package/src/channels/slack/slack-channel.ts +7 -0
  204. package/src/channels/slack/types.ts +47 -1
  205. package/src/index.ts +9 -0
  206. package/src/internal/cli-ax.ts +25 -1
  207. package/src/internal/cli-deploy.ts +22 -4
  208. package/src/internal/cli-mcp-oauth.ts +3 -1
  209. package/src/internal/cli-mcp.ts +4 -4
  210. package/src/internal/cursor-event-relay.ts +168 -0
  211. package/src/internal/deploy-client.ts +1 -1
  212. package/src/internal/discovery.ts +42 -5
  213. package/src/internal/distribution.ts +3 -1
  214. package/src/internal/durable-session-store.ts +95 -0
  215. package/src/internal/hosting.ts +2 -2
  216. package/src/internal/init-project.ts +151 -2
  217. package/src/internal/ledger-serving.ts +240 -0
  218. package/src/internal/listen-port.ts +85 -0
  219. package/src/internal/mcp-oauth.ts +29 -4
  220. package/src/internal/playground/static.ts +111 -20
  221. package/src/internal/sdk-runner.ts +11 -1
  222. package/src/internal/server.ts +76 -62
  223. package/src/internal/session-engine.ts +192 -16
  224. package/src/internal/slack-provision-client.ts +1 -1
  225. package/src/internal/workspace.ts +14 -3
  226. package/src/kv/memory-kv.ts +99 -0
  227. package/src/kv.ts +27 -0
  228. package/src/ledger/session-ledger.ts +535 -0
  229. package/src/ledger.ts +29 -0
  230. package/src/storage-backends/cursor-hosted.ts +108 -3
  231. package/src/storage-backends/file-kv.ts +128 -12
  232. package/src/storage.ts +90 -6
  233. package/src/types.ts +130 -4
  234. package/dist/channels/slack/post-update-delivery.d.ts +0 -85
  235. package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
  236. package/dist/docs/assets/chunks/@localSearchIndexroot.B-VTH4As.js +0 -1
  237. package/dist/docs/assets/example-agents_index.md.DgGBwckv.js +0 -2
  238. package/dist/docs/assets/index.md.CmhptOmN.js +0 -24
  239. package/dist/docs/assets/troubleshooting.md.B5RVX_tL.js +0 -1
  240. package/dist/internal/json-dir-store.d.ts +0 -32
  241. package/dist/internal/json-dir-store.d.ts.map +0 -1
  242. package/dist/internal/persistence-coordinator.d.ts +0 -127
  243. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  244. package/dist/multi-tenant.d.ts +0 -80
  245. package/dist/multi-tenant.d.ts.map +0 -1
  246. package/dist/multi-tenant.js +0 -69
  247. package/dist/persistence.d.ts +0 -184
  248. package/dist/persistence.d.ts.map +0 -1
  249. package/dist/playground/assets/index-BRhBbnd4.css +0 -1
  250. package/dist/playground/assets/index-_2WKw9a-.js +0 -85
  251. package/dist/storage-backends/postgres-kv.d.ts +0 -82
  252. package/dist/storage-backends/postgres-kv.d.ts.map +0 -1
  253. package/dist/storage-backends/postgres-kv.js +0 -185
  254. package/src/storage-backends/postgres-kv.ts +0 -253
  255. /package/dist/docs/assets/{ab.md.6cLOW7--.lean.js → ab.md.hdxsvX5M.lean.js} +0 -0
  256. /package/dist/docs/assets/{building-with-agents.md.txrcGU2B.lean.js → building-with-agents.md.PeZaZA1P.lean.js} +0 -0
  257. /package/dist/docs/assets/{evals.md.BQXI3rXy.lean.js → evals.md.qeoGgPw0.lean.js} +0 -0
  258. /package/dist/docs/assets/{example-agents_index.md.DgGBwckv.lean.js → example-agents_index.md.xmJ87d_3.lean.js} +0 -0
  259. /package/dist/docs/assets/{quickstart.md.C_b6ESpD.lean.js → quickstart.md.rIbfahJM.lean.js} +0 -0
@@ -117,6 +117,12 @@ export interface DeployCliOptions {
117
117
  err?: (text: string) => void;
118
118
  /** `secrets set` value input (hidden prompt on a TTY, lines otherwise). */
119
119
  stdin?: SecretInputStream;
120
+ /**
121
+ * Opt in to `NAME=VALUE` argv secrets. Default is names only (hidden
122
+ * prompt / stdin) so values never land in shell history or agent-captured
123
+ * terminals.
124
+ */
125
+ fromArgv?: boolean;
120
126
  }
121
127
 
122
128
  /** Minimal stdin surface `secrets set` reads values from (test-injectable). */
@@ -1143,8 +1149,9 @@ export async function cmdSecretsSet(
1143
1149
  const usageErr = options.err ?? ((t: string) => void process.stderr.write(t));
1144
1150
  if (slug === undefined || slug === "" || specs.length === 0) {
1145
1151
  usageErr(
1146
- `Usage: ${CLI} secrets set <slug> NAME[=VALUE] [NAME2[=VALUE2] ...] [--team <id>] [--json]\n` +
1147
- `NAMEs without =VALUE are prompted for (hidden input; reads stdin when piped).\n`
1152
+ `Usage: ${CLI} secrets set <slug> NAME [NAME2 ...] [--team <id>] [--json]\n` +
1153
+ ` Enter values at the hidden prompt, or pipe one line per NAME.\n` +
1154
+ ` Opt-in (leaks into argv / agent terminals): NAME=VALUE … --from-argv\n`
1148
1155
  );
1149
1156
  return 1;
1150
1157
  }
@@ -1162,9 +1169,20 @@ export async function cmdSecretsSet(
1162
1169
  }
1163
1170
  parsed.push(eq === -1 ? { name } : { name, value: spec.slice(eq + 1) });
1164
1171
  }
1165
- if (parsed.some((entry) => entry.value !== undefined)) {
1172
+ const hasArgvValues = parsed.some((entry) => entry.value !== undefined);
1173
+ if (hasArgvValues && options.fromArgv !== true) {
1174
+ usageErr(
1175
+ `Refusing NAME=VALUE on argv (shows up in shell history and agent-captured terminals).\n` +
1176
+ `Pass names only:\n` +
1177
+ ` ${CLI} secrets set ${slug} NAME\n` +
1178
+ `Hidden prompt on a TTY; one stdin line per NAME when piped.\n` +
1179
+ `Or redirect a file: ${CLI} secrets set ${slug} NAME < ./secret.txt\n`
1180
+ );
1181
+ return 1;
1182
+ }
1183
+ if (hasArgvValues) {
1166
1184
  usageErr(
1167
- `warning: secret values passed as arguments can end up in shell history; prefer \`${CLI} secrets set ${slug} NAME\` and enter the value at the hidden prompt.\n`
1185
+ `warning: --from-argv puts secret values in argv (shell history and agent-captured terminals).\n`
1168
1186
  );
1169
1187
  }
1170
1188
 
@@ -209,7 +209,9 @@ export async function authorizeHostMcpOauth(
209
209
 
210
210
  let callback: Awaited<ReturnType<typeof waitForMcpOAuthCallback>>;
211
211
  try {
212
- callback = await waitForMcpOAuthCallback();
212
+ callback = await waitForMcpOAuthCallback({
213
+ authorizationUrl: redirect.toString(),
214
+ });
213
215
  } catch (error) {
214
216
  err(`${LOG_PREFIX} OAuth callback failed: ${describe(error)}\n`);
215
217
  return { ok: false };
@@ -654,15 +654,15 @@ async function cmdMcpInstallRemote(options: McpCliOptions): Promise<number> {
654
654
  }
655
655
 
656
656
  // Verify the deployment resolves before writing config that cannot work.
657
- // This read is admin-tier, but calling the gateway only needs team
658
- // membership so a 403 means "cannot verify", not "cannot use": warn and
659
- // still write the entry rather than locking members out of their own agent.
657
+ // A 403 here is team membership or cloud-agent entitlement, not an
658
+ // admin-tier read. Still write the entry: calling the gateway only
659
+ // needs the API key.
660
660
  try {
661
661
  await auth.client.getDeployment({ teamId: auth.teamId, slug });
662
662
  } catch (error) {
663
663
  if (error instanceof DeployApiError && error.status === 403) {
664
664
  err(
665
- `Could not verify deployment "${slug}" (reading deployments needs team-admin); writing the entry anyway.\n`
665
+ `Could not verify deployment "${slug}" (not authorized for this team); writing the entry anyway.\n`
666
666
  );
667
667
  } else {
668
668
  err(`${describeError(error)}\n`);
@@ -57,6 +57,40 @@ export type CursorScmPullRequestMeta = {
57
57
  baseSha?: string;
58
58
  };
59
59
 
60
+ /** PR ref on a Checks / Actions webhook object. */
61
+ export type CursorScmCiPullRequestMeta = {
62
+ number: number;
63
+ url?: string;
64
+ };
65
+
66
+ export type CursorScmCiAppMeta = {
67
+ slug?: string;
68
+ name?: string;
69
+ };
70
+
71
+ /** Shared Checks / Actions fields. `id` + `headSha` satisfy the channel Zod schemas. */
72
+ type CursorScmCiBaseMeta = {
73
+ id: number;
74
+ headSha: string;
75
+ status?: string;
76
+ conclusion?: string | null;
77
+ htmlUrl?: string;
78
+ app?: CursorScmCiAppMeta;
79
+ pullRequests: CursorScmCiPullRequestMeta[];
80
+ };
81
+
82
+ export type CursorScmCheckRunMeta = CursorScmCiBaseMeta & {
83
+ name?: string;
84
+ checkSuiteId?: number;
85
+ };
86
+
87
+ export type CursorScmCheckSuiteMeta = CursorScmCiBaseMeta;
88
+
89
+ export type CursorScmWorkflowRunMeta = CursorScmCiBaseMeta & {
90
+ name?: string;
91
+ workflowId?: number;
92
+ };
93
+
60
94
  /** One event delivered by `GET /v0/scm-events` (metadata only). */
61
95
  export interface CursorScmEvent {
62
96
  offset: string;
@@ -81,6 +115,12 @@ export interface CursorScmEvent {
81
115
  * Zod-valid `issue_comment` / `pull_request_review_comment` webhook body.
82
116
  */
83
117
  comment?: CursorScmCommentMeta;
118
+ /** `check_run` snapshot; enough to stub `onCheckRun`. */
119
+ checkRun?: CursorScmCheckRunMeta;
120
+ /** `check_suite` snapshot; enough to stub `onCheckSuite`. */
121
+ checkSuite?: CursorScmCheckSuiteMeta;
122
+ /** `workflow_run` snapshot; enough to stub `onWorkflowRun`. */
123
+ workflowRun?: CursorScmWorkflowRunMeta;
84
124
  /** GitHub-side event time from the payload, when present. */
85
125
  occurredAtMs?: number;
86
126
  }
@@ -138,12 +178,37 @@ export function formatCursorScmEventLog(event: {
138
178
  return `${event.eventName}${action} on ${pr} received=${received}${occurred} (${delivery})`;
139
179
  }
140
180
 
181
+ /** Relay log suffix for one fan-out target, including parse errors on 4xx. */
182
+ export function formatCursorScmDispatchOutcome(target: {
183
+ slug: string;
184
+ status: number;
185
+ body: unknown;
186
+ }): string {
187
+ const slug = target.slug === "" ? "default" : target.slug;
188
+ if (target.status < 400) {
189
+ return `${slug}:${target.status}`;
190
+ }
191
+ const error = readDispatchError(target.body);
192
+ return error === undefined
193
+ ? `${slug}:${target.status}`
194
+ : `${slug}:${target.status} ${error}`;
195
+ }
196
+
197
+ function readDispatchError(body: unknown): string | undefined {
198
+ if (typeof body !== "object" || body === null) return undefined;
199
+ if (!("error" in body)) return undefined;
200
+ const error = body.error;
201
+ return typeof error === "string" && error !== "" ? error : undefined;
202
+ }
203
+
141
204
  /**
142
205
  * Minimal GitHub webhook body from an SCM-events metadata envelope.
143
206
  * When `pullRequest` has title + head/base refs+shas, emits a Zod-valid
144
207
  * `pull_request` object for `onPullRequest`; otherwise identity-only stubs.
145
208
  * When `comment` is present, emits a Zod-valid `comment` object so relayed
146
209
  * `issue_comment` / `pull_request_review_comment` deliveries dispatch.
210
+ * When a CI snapshot is present, emits `check_run` / `check_suite` /
211
+ * `workflow_run` so those typed hooks parse on the `--cursor-events` path.
147
212
  */
148
213
  export function scmMetadataToGitHubPayload(event: {
149
214
  repo: string;
@@ -152,6 +217,9 @@ export function scmMetadataToGitHubPayload(event: {
152
217
  prUrl?: string;
153
218
  pullRequest?: CursorScmPullRequestMeta;
154
219
  comment?: CursorScmCommentMeta;
220
+ checkRun?: CursorScmCheckRunMeta;
221
+ checkSuite?: CursorScmCheckSuiteMeta;
222
+ workflowRun?: CursorScmWorkflowRunMeta;
155
223
  }): Record<string, unknown> {
156
224
  const slash = event.repo.indexOf("/");
157
225
  const owner = slash === -1 ? "" : event.repo.slice(0, slash);
@@ -210,6 +278,30 @@ export function scmMetadataToGitHubPayload(event: {
210
278
  payload.comment = comment;
211
279
  }
212
280
 
281
+ const checkPullRequests = stubGitHubCiPullRequests({
282
+ snapshot:
283
+ event.checkRun?.pullRequests ??
284
+ event.checkSuite?.pullRequests ??
285
+ event.workflowRun?.pullRequests,
286
+ prNumber,
287
+ prUrl,
288
+ });
289
+ if (event.checkRun !== undefined) {
290
+ payload.check_run = stubGitHubCheckRun(event.checkRun, checkPullRequests);
291
+ }
292
+ if (event.checkSuite !== undefined) {
293
+ payload.check_suite = stubGitHubCheckSuite(
294
+ event.checkSuite,
295
+ checkPullRequests
296
+ );
297
+ }
298
+ if (event.workflowRun !== undefined) {
299
+ payload.workflow_run = stubGitHubWorkflowRun(
300
+ event.workflowRun,
301
+ checkPullRequests
302
+ );
303
+ }
304
+
213
305
  const title = prMeta?.title;
214
306
  const headRef = prMeta?.headRef;
215
307
  const headSha = prMeta?.headSha;
@@ -261,6 +353,82 @@ export function scmMetadataToGitHubPayload(event: {
261
353
  return payload;
262
354
  }
263
355
 
356
+ function stubGitHubCiPullRequests(input: {
357
+ snapshot: CursorScmCiPullRequestMeta[] | undefined;
358
+ prNumber: number | undefined;
359
+ prUrl: string | undefined;
360
+ }): Array<{ number: number; html_url?: string }> {
361
+ if (input.snapshot !== undefined && input.snapshot.length > 0) {
362
+ return input.snapshot.map((pr) => ({
363
+ number: pr.number,
364
+ ...(pr.url === undefined ? {} : { html_url: pr.url }),
365
+ }));
366
+ }
367
+ if (input.prNumber === undefined) return [];
368
+ return [
369
+ {
370
+ number: input.prNumber,
371
+ ...(input.prUrl === undefined ? {} : { html_url: input.prUrl }),
372
+ },
373
+ ];
374
+ }
375
+
376
+ function stubGitHubCiApp(
377
+ app: CursorScmCiAppMeta | undefined
378
+ ): Record<string, unknown> | undefined {
379
+ if (app === undefined) return undefined;
380
+ const out: Record<string, unknown> = {};
381
+ if (app.slug !== undefined) out.slug = app.slug;
382
+ if (app.name !== undefined) out.name = app.name;
383
+ return out;
384
+ }
385
+
386
+ function stubGitHubCiBase(
387
+ meta: CursorScmCiBaseMeta,
388
+ pullRequests: Array<{ number: number; html_url?: string }>
389
+ ): Record<string, unknown> {
390
+ const object: Record<string, unknown> = {
391
+ id: meta.id,
392
+ head_sha: meta.headSha,
393
+ };
394
+ if (meta.status !== undefined) object.status = meta.status;
395
+ if (meta.conclusion !== undefined) object.conclusion = meta.conclusion;
396
+ if (meta.htmlUrl !== undefined) object.html_url = meta.htmlUrl;
397
+ const app = stubGitHubCiApp(meta.app);
398
+ if (app !== undefined) object.app = app;
399
+ if (pullRequests.length > 0) object.pull_requests = pullRequests;
400
+ return object;
401
+ }
402
+
403
+ function stubGitHubCheckRun(
404
+ meta: CursorScmCheckRunMeta,
405
+ pullRequests: Array<{ number: number; html_url?: string }>
406
+ ): Record<string, unknown> {
407
+ const object = stubGitHubCiBase(meta, pullRequests);
408
+ if (meta.name !== undefined) object.name = meta.name;
409
+ if (meta.checkSuiteId !== undefined) {
410
+ object.check_suite = { id: meta.checkSuiteId };
411
+ }
412
+ return object;
413
+ }
414
+
415
+ function stubGitHubCheckSuite(
416
+ meta: CursorScmCheckSuiteMeta,
417
+ pullRequests: Array<{ number: number; html_url?: string }>
418
+ ): Record<string, unknown> {
419
+ return stubGitHubCiBase(meta, pullRequests);
420
+ }
421
+
422
+ function stubGitHubWorkflowRun(
423
+ meta: CursorScmWorkflowRunMeta,
424
+ pullRequests: Array<{ number: number; html_url?: string }>
425
+ ): Record<string, unknown> {
426
+ const object = stubGitHubCiBase(meta, pullRequests);
427
+ if (meta.name !== undefined) object.name = meta.name;
428
+ if (meta.workflowId !== undefined) object.workflow_id = meta.workflowId;
429
+ return object;
430
+ }
431
+
264
432
  /**
265
433
  * GitHub SCM-events relay: a thin {@link CursorRelayCore} adapter. Consumers
266
434
  * keep the same `new CursorEventRelay(options).start()/.stop()` surface.
@@ -705,7 +705,7 @@ function mapErrorResponse(
705
705
  );
706
706
  case 403:
707
707
  return new DeployApiError(
708
- `${serverError ?? "Not authorized"} — deploying needs team-admin permission and the cloud-agent entitlement on that team.`,
708
+ `${serverError ?? "Not authorized"} — deploying needs team membership and the cloud-agent entitlement on that team.`,
709
709
  403
710
710
  );
711
711
  case 404: {
@@ -28,6 +28,7 @@ import {
28
28
  type AgentProject,
29
29
  type AgentProjectInfo,
30
30
  type AgentRuntime,
31
+ type AgentServingMode,
31
32
  type ArtifactsDefinition,
32
33
  type BuiltinToolsConfig,
33
34
  type ChannelDefinition,
@@ -188,11 +189,20 @@ export async function loadAgentProjects(
188
189
  );
189
190
  }
190
191
  const results: Array<{ slug: string; project: AgentProject }> = [];
192
+ // Storage namespaces (session mirror, host kv, durable-session ledger)
193
+ // key on the project NAME, not the mount slug — two co-hosted agents
194
+ // sharing a name would silently share sessions and coordination state.
195
+ const slugByName = new Map<string, string>();
191
196
  for (const child of children) {
192
- results.push({
193
- slug: child.slug,
194
- project: await loadAgentProject(child.dir, options),
195
- });
197
+ const project = await loadAgentProject(child.dir, options);
198
+ const priorSlug = slugByName.get(project.name);
199
+ if (priorSlug !== undefined) {
200
+ throw new Error(
201
+ `Duplicate agent name "${project.name}" under ${resolved}: agents "${priorSlug}" and "${child.slug}" both resolve to it (package.json "name", or the directory name when unset). Co-hosted agents must have distinct names — their storage and session namespaces key on the name.`
202
+ );
203
+ }
204
+ slugByName.set(project.name, child.slug);
205
+ results.push({ slug: child.slug, project });
196
206
  }
197
207
  return results;
198
208
  }
@@ -420,6 +430,7 @@ async function loadAgent(
420
430
  };
421
431
  const hosting = resolveHosting(ctx, agentDir, config?.hosting);
422
432
  const concurrency = resolveConcurrency(ctx, agentDir, config?.concurrency);
433
+ const serving = resolveServing(ctx, agentDir, config?.serving);
423
434
 
424
435
  if (runtime === "cloud") {
425
436
  warnCloudRuntimeLimitations(ctx, agentDir, {
@@ -443,6 +454,7 @@ async function loadAgent(
443
454
  local,
444
455
  hosting,
445
456
  concurrency,
457
+ serving,
446
458
  tools,
447
459
  skills,
448
460
  connections,
@@ -451,6 +463,27 @@ async function loadAgent(
451
463
  };
452
464
  }
453
465
 
466
+ /**
467
+ * Validate the authored serving mode. An unrecognized value is an error
468
+ * diagnostic and falls back to undefined (resident) — a typo must not
469
+ * silently opt an agent into (or out of) ledger coordination.
470
+ */
471
+ function resolveServing(
472
+ ctx: DiscoveryContext,
473
+ agentDir: string,
474
+ serving: AgentServingMode | undefined
475
+ ): AgentServingMode | undefined {
476
+ if (serving === undefined || serving === "resident" || serving === "ledger") {
477
+ return serving;
478
+ }
479
+ ctx.diagnostics.push({
480
+ severity: "error",
481
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
482
+ message: `serving must be "resident" or "ledger"; got ${JSON.stringify(serving)}.`,
483
+ });
484
+ return undefined;
485
+ }
486
+
454
487
  /** Absolute harness cwd parent, or undefined when unset / blank. */
455
488
  function resolveAgentCwd(
456
489
  ctx: DiscoveryContext,
@@ -504,7 +537,7 @@ function resolveHosting(
504
537
  warn(`hosting.secretNames: ${nameError}`);
505
538
  } else if (isReservedSecretName(name)) {
506
539
  warn(
507
- `hosting.secretNames: ${JSON.stringify(name)} is platform-managed (CURSOR_* / Cursor-hosted storage) and injected automatically; omit it from secretNames.`
540
+ `hosting.secretNames: ${JSON.stringify(name)} is platform-managed (CURSOR_* / reserved hosted-storage names); omit it from secretNames.`
508
541
  );
509
542
  }
510
543
  }
@@ -1863,6 +1896,10 @@ export function projectInfo(project: AgentProject): AgentProjectInfo {
1863
1896
  name: connection.name,
1864
1897
  transport: connectionTransportLabel(connection.transport),
1865
1898
  hostOnly: connection.hostOnly,
1899
+ cursorAccountServers:
1900
+ "cursorAccount" in connection.transport
1901
+ ? (connection.transport.servers ?? "*")
1902
+ : undefined,
1866
1903
  })),
1867
1904
  subagents: agent.subagents.map((subagent) => ({
1868
1905
  name: subagent.name,
@@ -108,8 +108,10 @@ export const AUTHORING_ENTRY_FILES: ReadonlyArray<
108
108
  ["evals/loaders", "evals/loaders.js"],
109
109
  ["ab", "ab.js"],
110
110
  ["artifacts", "artifacts.js"],
111
+ ["kv", "kv.js"],
112
+ ["kv/memory", "kv/memory-kv.js"],
113
+ ["ledger", "ledger.js"],
111
114
  ["storage", "storage.js"],
112
115
  ["storage/file-kv", "storage-backends/file-kv.js"],
113
- ["storage/postgres-kv", "storage-backends/postgres-kv.js"],
114
116
  ["storage/cursor-hosted", "storage-backends/cursor-hosted.js"],
115
117
  ];
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Durable-session store resolution.
3
+ *
4
+ * Two APIs, one backend: `defineStorage` is the **storage API** (agent
5
+ * state — session records, event chunks, author KV), and the session
6
+ * ledger is the **durable session API** (session identity, leases,
7
+ * fenced commits). The ledger consumes {@link SessionLedgerKv} — exactly
8
+ * the three CAS methods it uses, nothing more; this module adapts a
9
+ * storage backend's `cas` group into that interface, so a deployment's
10
+ * one authored backend serves both APIs by default.
11
+ *
12
+ * Calls go straight at the config's methods — NOT through the
13
+ * StorageCoordinator: the coordinator's queue is bounded, serialized, and
14
+ * at-most-once (mirror semantics), while ledger CAS calls are the
15
+ * correctness path and must observe their own results synchronously.
16
+ */
17
+
18
+ import type { SessionLedgerKv } from "../ledger/session-ledger.js";
19
+ import {
20
+ keySegment,
21
+ STORAGE_KEY_ROOT,
22
+ type StorageConfig,
23
+ type StorageContext,
24
+ } from "../storage.js";
25
+
26
+ export interface StorageCasKvArgs {
27
+ agentName: string;
28
+ projectRoot: string;
29
+ }
30
+
31
+ /**
32
+ * Adapt a storage backend's `cas` group into the ledger's KV interface.
33
+ * Throws when the backend does not implement `cas` — the caller turns
34
+ * that into the fail-fast startup error for `serving: "ledger"`. Every
35
+ * call carries a `reason: "coordination"` {@link StorageContext}.
36
+ */
37
+ export function storageCasKv(
38
+ config: StorageConfig,
39
+ args: StorageCasKvArgs
40
+ ): SessionLedgerKv {
41
+ const ctx: StorageContext = {
42
+ agentName: args.agentName,
43
+ projectRoot: args.projectRoot,
44
+ reason: "coordination",
45
+ };
46
+ const { cas } = config;
47
+ if (cas === undefined) {
48
+ throw new Error(
49
+ "storageCasKv: the storage backend does not implement the cas group (conditional writes)"
50
+ );
51
+ }
52
+ return {
53
+ async getWithVersion(key) {
54
+ return await cas.getWithVersion(key, ctx);
55
+ },
56
+ async putIfAbsent(key, value) {
57
+ return await cas.putIfAbsent(key, value, ctx);
58
+ },
59
+ async putIfVersion(key, value, expectedVersion) {
60
+ return await cas.putIfVersion(key, value, expectedVersion, ctx);
61
+ },
62
+ };
63
+ }
64
+
65
+ /**
66
+ * The one place the ledger's physical namespace is minted:
67
+ * `agentkit/v1/<agent>/durable-session/` — the {@link STORAGE_KEY_ROOT}
68
+ * conventions with the agent segment encoded like every other
69
+ * caller-controlled segment. The ledger itself never sees this string;
70
+ * it speaks bare `session/…` / `ledger/…` keys against a handle that
71
+ * {@link namespacedKv} has already scoped.
72
+ */
73
+ export function ledgerKeyPrefix(agentName: string): string {
74
+ return `${STORAGE_KEY_ROOT}/${keySegment(agentName)}/durable-session/`;
75
+ }
76
+
77
+ /**
78
+ * Scope a raw store handle under a namespace, prepending it to every
79
+ * key on the way in. Applied exactly once, where the engine constructs
80
+ * the ledger's store handle — the same conceptual boundary where
81
+ * `host.kv` gets its agent scoping. Internal: not part of the public kv
82
+ * interface, and callers (including the explicit `durableSessionStore`
83
+ * override) always supply the raw store — they never write prefixes.
84
+ */
85
+ export function namespacedKv(
86
+ store: SessionLedgerKv,
87
+ namespace: string
88
+ ): SessionLedgerKv {
89
+ return {
90
+ getWithVersion: (key) => store.getWithVersion(namespace + key),
91
+ putIfAbsent: (key, value) => store.putIfAbsent(namespace + key, value),
92
+ putIfVersion: (key, value, expectedVersion) =>
93
+ store.putIfVersion(namespace + key, value, expectedVersion),
94
+ };
95
+ }
@@ -15,7 +15,7 @@ export const MAX_SECRETS = 32;
15
15
  export const MAX_SECRET_VALUE_BYTES = 4096;
16
16
  /** Max secret name length. */
17
17
  export const MAX_SECRET_NAME_LENGTH = 64;
18
- /** Secret names the platform injects; user writes are rejected. */
18
+ /** Secret names the platform manages; user writes are rejected. */
19
19
  export const RESERVED_SECRET_NAME_PREFIX = "CURSOR_";
20
20
 
21
21
  /**
@@ -84,7 +84,7 @@ export function validateSettableSecretName(name: string): string | undefined {
84
84
  return nameError;
85
85
  }
86
86
  if (isReservedSecretName(name)) {
87
- return `Secret name ${JSON.stringify(name)} is platform-managed; the platform injects CURSOR_* and Cursor-hosted storage values automatically.`;
87
+ return `Secret name ${JSON.stringify(name)} is platform-managed; user secrets cannot claim CURSOR_* or reserved hosted-storage names.`;
88
88
  }
89
89
  return undefined;
90
90
  }