@cursor/july 0.1.2 → 0.1.4

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 (232) hide show
  1. package/AGENTS.md +16 -5
  2. package/README.md +8 -9
  3. package/dist/bin/agent-serve.d.ts +3 -1
  4. package/dist/bin/agent-serve.d.ts.map +1 -1
  5. package/dist/bin/agent-serve.js +416 -155
  6. package/dist/channels/slack/log.d.ts +1 -1
  7. package/dist/channels/slack/log.d.ts.map +1 -1
  8. package/dist/channels/slack/log.js +4 -5
  9. package/dist/channels/slack/setup.js +1 -1
  10. package/dist/channels/slack/socket-mode.d.ts.map +1 -1
  11. package/dist/channels/slack/socket-mode.js +10 -9
  12. package/dist/docs/404.html +2 -2
  13. package/dist/docs/ab.html +3 -3
  14. package/dist/docs/assets/{app.Oje4vhlk.js → app.DF-Yx4OP.js} +1 -1
  15. package/dist/docs/assets/chunks/@localSearchIndexroot.0XrheBv2.js +1 -0
  16. package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.DSeJSudL.js} +1 -1
  17. package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.DZVLTXOC.js} +2 -2
  18. package/dist/docs/assets/{index.md.m81y7TY7.js → index.md.Cylk70gg.js} +1 -1
  19. package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
  20. package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
  21. package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
  22. package/dist/docs/building-with-agents.html +3 -3
  23. package/dist/docs/concepts.html +3 -3
  24. package/dist/docs/deployment.html +3 -3
  25. package/dist/docs/evals.html +3 -3
  26. package/dist/docs/guides/agent-to-agent.html +3 -3
  27. package/dist/docs/guides/cloud-runtime.html +3 -3
  28. package/dist/docs/guides/github.html +3 -3
  29. package/dist/docs/guides/human-in-the-loop.html +3 -3
  30. package/dist/docs/guides/slack.html +3 -3
  31. package/dist/docs/guides/webhooks.html +3 -3
  32. package/dist/docs/hashmap.json +1 -1
  33. package/dist/docs/hillclimbing.html +3 -3
  34. package/dist/docs/index.html +5 -5
  35. package/dist/docs/quickstart.html +22 -10
  36. package/dist/docs/reference/agent-config.html +3 -3
  37. package/dist/docs/reference/channels.html +3 -3
  38. package/dist/docs/reference/cli.html +14 -9
  39. package/dist/docs/reference/connections.html +3 -3
  40. package/dist/docs/reference/hooks.html +3 -3
  41. package/dist/docs/reference/http-api.html +3 -3
  42. package/dist/docs/reference/instructions.html +3 -3
  43. package/dist/docs/reference/playground.html +3 -3
  44. package/dist/docs/reference/project-layout.html +3 -3
  45. package/dist/docs/reference/schedules.html +3 -3
  46. package/dist/docs/reference/sessions.html +3 -3
  47. package/dist/docs/reference/skills.html +3 -3
  48. package/dist/docs/reference/subagents.html +3 -3
  49. package/dist/docs/reference/tools.html +3 -3
  50. package/dist/docs/scaffolding-agents.html +3 -3
  51. package/dist/docs/storage.html +3 -3
  52. package/dist/docs/troubleshooting.html +3 -3
  53. package/dist/internal/ab-collector.js +3 -3
  54. package/dist/internal/ab-fold.js +1 -1
  55. package/dist/internal/chat-client.d.ts +29 -3
  56. package/dist/internal/chat-client.d.ts.map +1 -1
  57. package/dist/internal/chat-client.js +180 -27
  58. package/dist/internal/cli-ax.d.ts +83 -2
  59. package/dist/internal/cli-ax.d.ts.map +1 -1
  60. package/dist/internal/cli-ax.js +626 -52
  61. package/dist/internal/cli-cursor.d.ts.map +1 -1
  62. package/dist/internal/cli-cursor.js +8 -36
  63. package/dist/internal/cli-deploy.d.ts +59 -3
  64. package/dist/internal/cli-deploy.d.ts.map +1 -1
  65. package/dist/internal/cli-deploy.js +316 -38
  66. package/dist/internal/cli-github.js +13 -13
  67. package/dist/internal/cursor/account-mcp.js +3 -3
  68. package/dist/internal/cursor/backend-client.js +1 -1
  69. package/dist/internal/cursor/credentials.d.ts +14 -3
  70. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  71. package/dist/internal/cursor/credentials.js +21 -6
  72. package/dist/internal/cursor/github-credentials.js +3 -3
  73. package/dist/internal/cursor-event-relay.js +1 -1
  74. package/dist/internal/cursor-relay-core.d.ts +1 -1
  75. package/dist/internal/cursor-relay-core.d.ts.map +1 -1
  76. package/dist/internal/cursor-relay-core.js +2 -1
  77. package/dist/internal/cursor-slack-relay.js +1 -1
  78. package/dist/internal/deploy-client.d.ts +9 -1
  79. package/dist/internal/deploy-client.d.ts.map +1 -1
  80. package/dist/internal/deploy-client.js +23 -3
  81. package/dist/internal/deploy-source.d.ts +35 -0
  82. package/dist/internal/deploy-source.d.ts.map +1 -0
  83. package/dist/internal/deploy-source.js +118 -0
  84. package/dist/internal/discovery.d.ts +11 -0
  85. package/dist/internal/discovery.d.ts.map +1 -1
  86. package/dist/internal/discovery.js +43 -14
  87. package/dist/internal/distribution.d.ts +7 -0
  88. package/dist/internal/distribution.d.ts.map +1 -1
  89. package/dist/internal/distribution.js +7 -0
  90. package/dist/internal/docs-site.js +3 -3
  91. package/dist/internal/eval-run-store.d.ts.map +1 -1
  92. package/dist/internal/eval-run-store.js +10 -9
  93. package/dist/internal/eval-runner.d.ts +2 -0
  94. package/dist/internal/eval-runner.d.ts.map +1 -1
  95. package/dist/internal/eval-runner.js +2 -0
  96. package/dist/internal/event-mapper.d.ts +54 -1
  97. package/dist/internal/event-mapper.d.ts.map +1 -1
  98. package/dist/internal/event-mapper.js +151 -41
  99. package/dist/internal/github-fanout.js +2 -2
  100. package/dist/internal/host-platforms.js +5 -5
  101. package/dist/internal/init-project.d.ts +88 -1
  102. package/dist/internal/init-project.d.ts.map +1 -1
  103. package/dist/internal/init-project.js +221 -31
  104. package/dist/internal/install-cursor-skills.d.ts +64 -0
  105. package/dist/internal/install-cursor-skills.d.ts.map +1 -0
  106. package/dist/internal/install-cursor-skills.js +274 -0
  107. package/dist/internal/logs-client.d.ts +60 -0
  108. package/dist/internal/logs-client.d.ts.map +1 -0
  109. package/dist/internal/logs-client.js +311 -0
  110. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  111. package/dist/internal/mcp-endpoint.js +1 -1
  112. package/dist/internal/open-browser.d.ts +5 -0
  113. package/dist/internal/open-browser.d.ts.map +1 -0
  114. package/dist/internal/open-browser.js +34 -0
  115. package/dist/internal/playground/toolchain.d.ts.map +1 -1
  116. package/dist/internal/playground/toolchain.js +19 -14
  117. package/dist/internal/playground-proxy.d.ts +69 -0
  118. package/dist/internal/playground-proxy.d.ts.map +1 -0
  119. package/dist/internal/playground-proxy.js +468 -0
  120. package/dist/internal/prompt-context.d.ts +35 -0
  121. package/dist/internal/prompt-context.d.ts.map +1 -0
  122. package/dist/internal/prompt-context.js +71 -0
  123. package/dist/internal/reminder-runner.d.ts.map +1 -1
  124. package/dist/internal/reminder-runner.js +11 -11
  125. package/dist/internal/request-headers.d.ts +11 -0
  126. package/dist/internal/request-headers.d.ts.map +1 -0
  127. package/dist/internal/request-headers.js +14 -0
  128. package/dist/internal/resolve-prod-target.d.ts +44 -0
  129. package/dist/internal/resolve-prod-target.d.ts.map +1 -0
  130. package/dist/internal/resolve-prod-target.js +134 -0
  131. package/dist/internal/resolved-connections.js +5 -5
  132. package/dist/internal/run-client.d.ts +2 -2
  133. package/dist/internal/run-client.d.ts.map +1 -1
  134. package/dist/internal/run-client.js +38 -23
  135. package/dist/internal/schedule-runner.js +1 -1
  136. package/dist/internal/server.d.ts.map +1 -1
  137. package/dist/internal/server.js +23 -17
  138. package/dist/internal/session-engine.d.ts +1 -1
  139. package/dist/internal/session-engine.d.ts.map +1 -1
  140. package/dist/internal/session-engine.js +34 -19
  141. package/dist/internal/sessions-client.d.ts +34 -0
  142. package/dist/internal/sessions-client.d.ts.map +1 -0
  143. package/dist/internal/sessions-client.js +193 -0
  144. package/dist/internal/storage-coordinator.js +6 -6
  145. package/dist/internal/stream-progress.d.ts.map +1 -1
  146. package/dist/internal/stream-progress.js +31 -3
  147. package/dist/internal/terminal-style.d.ts +22 -0
  148. package/dist/internal/terminal-style.d.ts.map +1 -0
  149. package/dist/internal/terminal-style.js +49 -0
  150. package/dist/internal/trajectory.d.ts +10 -5
  151. package/dist/internal/trajectory.d.ts.map +1 -1
  152. package/dist/internal/trajectory.js +82 -10
  153. package/dist/internal/update-check.d.ts +93 -0
  154. package/dist/internal/update-check.d.ts.map +1 -0
  155. package/dist/internal/update-check.js +431 -0
  156. package/dist/internal/workspace.d.ts +11 -0
  157. package/dist/internal/workspace.d.ts.map +1 -1
  158. package/dist/internal/workspace.js +38 -4
  159. package/dist/playground/assets/index-Ct_Usdm8.css +1 -0
  160. package/dist/playground/assets/index-Dsyb-Vaj.js +319 -0
  161. package/dist/playground/index.html +2 -2
  162. package/dist/types.d.ts +29 -2
  163. package/dist/types.d.ts.map +1 -1
  164. package/docs/README.md +0 -1
  165. package/docs/quickstart.md +22 -7
  166. package/docs/reference/cli.md +51 -3
  167. package/package.json +4 -1
  168. package/skills/ab/SKILL.md +8 -8
  169. package/skills/create-agent/SKILL.md +28 -22
  170. package/skills/debug/SKILL.md +11 -11
  171. package/skills/evals/SKILL.md +16 -16
  172. package/skills/framework-map/SKILL.md +6 -6
  173. package/skills/github/SKILL.md +13 -13
  174. package/skills/hillclimb/SKILL.md +10 -10
  175. package/skills/setup-slack/SKILL.md +14 -14
  176. package/src/bin/agent-serve.ts +451 -191
  177. package/src/channels/slack/log.ts +5 -5
  178. package/src/channels/slack/setup.ts +1 -1
  179. package/src/channels/slack/socket-mode.ts +11 -9
  180. package/src/internal/ab-collector.ts +3 -3
  181. package/src/internal/ab-fold.ts +1 -1
  182. package/src/internal/chat-client.ts +252 -37
  183. package/src/internal/cli-ax.ts +761 -72
  184. package/src/internal/cli-cursor.ts +14 -42
  185. package/src/internal/cli-deploy.ts +428 -49
  186. package/src/internal/cli-github.ts +13 -15
  187. package/src/internal/cursor/account-mcp.ts +3 -3
  188. package/src/internal/cursor/backend-client.ts +1 -1
  189. package/src/internal/cursor/credentials.ts +26 -7
  190. package/src/internal/cursor/github-credentials.ts +3 -3
  191. package/src/internal/cursor-event-relay.ts +1 -1
  192. package/src/internal/cursor-relay-core.ts +4 -2
  193. package/src/internal/cursor-slack-relay.ts +1 -1
  194. package/src/internal/deploy-client.ts +45 -4
  195. package/src/internal/deploy-source.ts +133 -0
  196. package/src/internal/discovery.ts +53 -16
  197. package/src/internal/distribution.ts +8 -0
  198. package/src/internal/docs-site.ts +3 -3
  199. package/src/internal/eval-run-store.ts +10 -11
  200. package/src/internal/eval-runner.ts +5 -0
  201. package/src/internal/event-mapper.ts +222 -42
  202. package/src/internal/github-fanout.ts +2 -2
  203. package/src/internal/host-platforms.ts +5 -5
  204. package/src/internal/init-project.ts +333 -51
  205. package/src/internal/install-cursor-skills.ts +327 -0
  206. package/src/internal/logs-client.ts +442 -0
  207. package/src/internal/mcp-endpoint.ts +1 -3
  208. package/src/internal/open-browser.ts +41 -0
  209. package/src/internal/playground/toolchain.ts +18 -16
  210. package/src/internal/playground-proxy.ts +576 -0
  211. package/src/internal/prompt-context.ts +95 -0
  212. package/src/internal/reminder-runner.ts +11 -17
  213. package/src/internal/request-headers.ts +23 -0
  214. package/src/internal/resolve-prod-target.ts +182 -0
  215. package/src/internal/resolved-connections.ts +5 -5
  216. package/src/internal/run-client.ts +39 -36
  217. package/src/internal/schedule-runner.ts +1 -1
  218. package/src/internal/server.ts +25 -16
  219. package/src/internal/session-engine.ts +35 -18
  220. package/src/internal/sessions-client.ts +216 -0
  221. package/src/internal/storage-coordinator.ts +6 -6
  222. package/src/internal/stream-progress.ts +36 -2
  223. package/src/internal/terminal-style.ts +74 -0
  224. package/src/internal/trajectory.ts +91 -13
  225. package/src/internal/update-check.ts +552 -0
  226. package/src/internal/workspace.ts +42 -4
  227. package/src/types.ts +45 -7
  228. package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
  229. package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
  230. package/dist/playground/assets/index-CpDYCj8W.js +0 -79
  231. /package/dist/docs/assets/{index.md.m81y7TY7.lean.js → index.md.Cylk70gg.lean.js} +0 -0
  232. /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
@@ -196,7 +196,7 @@ async function cmdGithubForward(options: GithubCliOptions): Promise<number> {
196
196
  );
197
197
  return 1;
198
198
  }
199
- process.stderr.write(`[agent-serve] inferred --repo ${repo}\n`);
199
+ process.stderr.write(`[agentkit] inferred --repo ${repo}\n`);
200
200
  }
201
201
 
202
202
  const secret = options.secret ?? process.env.GITHUB_WEBHOOK_SECRET;
@@ -219,30 +219,30 @@ async function cmdGithubForward(options: GithubCliOptions): Promise<number> {
219
219
  });
220
220
 
221
221
  process.stderr.write(
222
- `[agent-serve] forwarding ${org !== undefined ? `org ${org}` : `repo ${repo}`} to ${targets.length} channel${targets.length === 1 ? "" : "s"}:\n`
222
+ `[agentkit] forwarding ${org !== undefined ? `org ${org}` : `repo ${repo}`} to ${targets.length} channel${targets.length === 1 ? "" : "s"}:\n`
223
223
  );
224
224
  for (const target of targets) {
225
225
  process.stderr.write(
226
- `[agent-serve] → ${targetLabel(target)} ${target.url} (${target.events.join(", ")})\n`
226
+ `[agentkit] → ${targetLabel(target)} ${target.url} (${target.events.join(", ")})\n`
227
227
  );
228
228
  }
229
229
  if (fanout) {
230
230
  process.stderr.write(
231
- `[agent-serve] fan-out proxy on ${forwardUrl} (union events: ${events.join(", ")})\n`
231
+ `[agentkit] fan-out proxy on ${forwardUrl} (union events: ${events.join(", ")})\n`
232
232
  );
233
233
  }
234
234
  if (secret === undefined || secret === "") {
235
235
  process.stderr.write(
236
- "[agent-serve] no signing secret — deliveries are unsigned. A `serve --dev` github channel accepts them; set GITHUB_WEBHOOK_SECRET on both sides to verify signatures (required for a non-dev target).\n"
236
+ "[agentkit] no signing secret — deliveries are unsigned. A `serve --dev` github channel accepts them; set GITHUB_WEBHOOK_SECRET on both sides to verify signatures (required for a non-dev target).\n"
237
237
  );
238
238
  }
239
239
  const envToken = detectGitHubEnvTokenOverride();
240
240
  if (envToken !== undefined) {
241
241
  process.stderr.write(
242
- `[agent-serve] warning: ${githubEnvTokenHint(envToken)}\n`
242
+ `[agentkit] warning: ${githubEnvTokenHint(envToken)}\n`
243
243
  );
244
244
  }
245
- process.stderr.write(`[agent-serve] running: gh ${redact(args)}\n`);
245
+ process.stderr.write(`[agentkit] running: gh ${redact(args)}\n`);
246
246
 
247
247
  try {
248
248
  return await spawnGh(args);
@@ -318,7 +318,7 @@ async function cmdGithubReplay(options: GithubCliOptions): Promise<number> {
318
318
  );
319
319
  if (unsupported.length > 0) {
320
320
  process.stderr.write(
321
- `[agent-serve] cannot synthesize (skipped): ${unsupported.join(", ")}\n`
321
+ `[agentkit] cannot synthesize (skipped): ${unsupported.join(", ")}\n`
322
322
  );
323
323
  }
324
324
  if (eventsToReplay.length === 0) {
@@ -364,9 +364,7 @@ async function cmdGithubReplay(options: GithubCliOptions): Promise<number> {
364
364
  check,
365
365
  });
366
366
  if ("error" in result) {
367
- process.stderr.write(
368
- `[agent-serve] skipping ${event}: ${result.error}\n`
369
- );
367
+ process.stderr.write(`[agentkit] skipping ${event}: ${result.error}\n`);
370
368
  continue;
371
369
  }
372
370
  synthesized.push(result);
@@ -378,7 +376,7 @@ async function cmdGithubReplay(options: GithubCliOptions): Promise<number> {
378
376
 
379
377
  const secret = options.secret ?? process.env.GITHUB_WEBHOOK_SECRET;
380
378
  process.stderr.write(
381
- `[agent-serve] replaying ${ref.label} (${synthesized
379
+ `[agentkit] replaying ${ref.label} (${synthesized
382
380
  .map((s) => s.eventName)
383
381
  .join(
384
382
  ", "
@@ -401,7 +399,7 @@ async function cmdGithubReplay(options: GithubCliOptions): Promise<number> {
401
399
 
402
400
  if (options.out !== undefined) {
403
401
  const file = await writeReplayFixture(options.out, event);
404
- process.stderr.write(`[agent-serve] wrote ${file}\n`);
402
+ process.stderr.write(`[agentkit] wrote ${file}\n`);
405
403
  }
406
404
 
407
405
  const label = eventLabel(event);
@@ -436,7 +434,7 @@ async function cmdGithubReplay(options: GithubCliOptions): Promise<number> {
436
434
  `${delivery.label} ${delivery.status || "ERR"}${delivery.ok ? "" : " (rejected)"}`
437
435
  )
438
436
  .join(", ") || "(no matching channel)";
439
- process.stderr.write(`[agent-serve] ${label} → ${summary}\n`);
437
+ process.stderr.write(`[agentkit] ${label} → ${summary}\n`);
440
438
  }
441
439
  }
442
440
 
@@ -747,7 +745,7 @@ async function ensureWebhookExtension(
747
745
  };
748
746
  }
749
747
  process.stderr.write(
750
- `[agent-serve] installing ${GITHUB_WEBHOOK_EXTENSION}@${GITHUB_WEBHOOK_EXTENSION_PIN}…\n`
748
+ `[agentkit] installing ${GITHUB_WEBHOOK_EXTENSION}@${GITHUB_WEBHOOK_EXTENSION_PIN}…\n`
751
749
  );
752
750
  if (
753
751
  (await tryExec({
@@ -231,7 +231,7 @@ export class CursorAccountMcpBridge {
231
231
  )
232
232
  .join(", ");
233
233
  this.logger(
234
- `[agent-serve] Cursor account MCP connection "${ref.name}": ${
234
+ `[agentkit] Cursor account MCP connection "${ref.name}": ${
235
235
  catalog.servers.length === 0
236
236
  ? "no matching connectors on this account — connect servers in Cursor (dashboard → MCP) or fix the `servers` filter"
237
237
  : summary
@@ -242,7 +242,7 @@ export class CursorAccountMcpBridge {
242
242
  );
243
243
  if (needsAuth.length > 0) {
244
244
  this.logger(
245
- `[agent-serve] Cursor account MCP connection "${ref.name}": ${needsAuth
245
+ `[agentkit] Cursor account MCP connection "${ref.name}": ${needsAuth
246
246
  .map((server) => server.serverIdentifier)
247
247
  .join(
248
248
  ", "
@@ -252,7 +252,7 @@ export class CursorAccountMcpBridge {
252
252
  })
253
253
  .catch((error: unknown) => {
254
254
  this.logger(
255
- `[agent-serve] Cursor account MCP connection "${ref.name}" preflight failed: ${describeError(error)}`
255
+ `[agentkit] Cursor account MCP connection "${ref.name}" preflight failed: ${describeError(error)}`
256
256
  );
257
257
  });
258
258
  }
@@ -445,7 +445,7 @@ export class CursorBackendClient {
445
445
  value: body.data.accessToken,
446
446
  expiresAtMs: jwtExpiryMs(body.data.accessToken),
447
447
  };
448
- this.logger("[agent-serve] cursor account: minted a fresh access token");
448
+ this.logger("[agentkit] cursor account: minted a fresh access token");
449
449
  return body.data.accessToken;
450
450
  }
451
451
  }
@@ -23,6 +23,7 @@ import { z } from "zod";
23
23
  import { CLI_COMMAND_NAME } from "../distribution.js";
24
24
 
25
25
  export const DEFAULT_CURSOR_BACKEND_URL = "https://api2.cursor.sh";
26
+ export const DEFAULT_CURSOR_EXTERNAL_API_URL = "https://api.cursor.com";
26
27
  const DEFAULT_CURSOR_WEBSITE_URL = "https://cursor.com";
27
28
 
28
29
  /** Backend API base URL (`CURSOR_API_BASE_URL` override for local dev). */
@@ -32,6 +33,19 @@ export function cursorBackendUrl(): string {
32
33
  );
33
34
  }
34
35
 
36
+ /**
37
+ * External API base URL for routes served only by the external-api
38
+ * microlith (`/internal/agent-serve/*`, engine aliases). In production
39
+ * these are mounted on api.cursor.com and 404 on api2.cursor.sh, so the
40
+ * deploy surface needs its own default. `CURSOR_API_BASE_URL` still
41
+ * overrides everything for local dev, where one server mounts all routes.
42
+ */
43
+ export function cursorExternalApiUrl(): string {
44
+ return trimTrailingSlash(
45
+ process.env.CURSOR_API_BASE_URL ?? DEFAULT_CURSOR_EXTERNAL_API_URL
46
+ );
47
+ }
48
+
35
49
  /** Website base URL for the login deep link (`CURSOR_WEBSITE_URL` override). */
36
50
  export function cursorWebsiteUrl(): string {
37
51
  return trimTrailingSlash(
@@ -59,18 +73,23 @@ const storedCredentialsSchema: z.ZodObject<
59
73
  export type StoredCredentials = z.infer<typeof storedCredentialsSchema>;
60
74
 
61
75
  /**
62
- * `~/.config/agent-serve/credentials.json` (0600), honoring
63
- * `XDG_CONFIG_HOME`. `AGENT_SERVE_CONFIG_DIR` pins the directory outright
64
- * (tests, containers).
76
+ * `~/.config/agent-serve/` (honoring `XDG_CONFIG_HOME`), the host-level
77
+ * config directory for credentials and the update-check cache.
78
+ * `AGENT_SERVE_CONFIG_DIR` pins the directory outright (tests, containers).
65
79
  */
66
- export function credentialsFilePath(): string {
67
- const configDir =
80
+ export function configDirPath(): string {
81
+ return (
68
82
  process.env.AGENT_SERVE_CONFIG_DIR ??
69
83
  join(
70
84
  process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"),
71
85
  "agent-serve"
72
- );
73
- return join(configDir, "credentials.json");
86
+ )
87
+ );
88
+ }
89
+
90
+ /** `<configDir>/credentials.json` (0600). */
91
+ export function credentialsFilePath(): string {
92
+ return join(configDirPath(), "credentials.json");
74
93
  }
75
94
 
76
95
  export async function readStoredCredentials(): Promise<
@@ -148,14 +148,14 @@ export class CursorGithubCredentialProvider {
148
148
  this.scheduleRefresh();
149
149
  }
150
150
  this.logger(
151
- `[agent-serve] GitHub auth: refreshed Cursor account credential for ${this.repos.length} repo(s)`
151
+ `[agentkit] GitHub auth: refreshed Cursor account credential for ${this.repos.length} repo(s)`
152
152
  );
153
153
  return result.token;
154
154
  } catch (error) {
155
155
  const message = error instanceof Error ? error.message : String(error);
156
156
  if (this.cached !== undefined && this.now() < this.cached.expiresAtMs) {
157
157
  this.logger(
158
- `[agent-serve] GitHub auth: Cursor account credential refresh failed; using the current lease: ${message}`
158
+ `[agentkit] GitHub auth: Cursor account credential refresh failed; using the current lease: ${message}`
159
159
  );
160
160
  this.cached.refreshAtMs = Math.min(
161
161
  this.cached.expiresAtMs,
@@ -207,7 +207,7 @@ export class CursorGithubCredentialProvider {
207
207
  this.refreshTimer = setTimeout(() => {
208
208
  void this.getToken().catch((error: unknown) => {
209
209
  this.logger(
210
- `[agent-serve] GitHub auth: Cursor account credential refresh failed: ${
210
+ `[agentkit] GitHub auth: Cursor account credential refresh failed: ${
211
211
  error instanceof Error ? error.message : String(error)
212
212
  }`
213
213
  );
@@ -263,7 +263,7 @@ export class CursorEventRelay {
263
263
  if (!this.loggedSkippedRepos.has(event.repo)) {
264
264
  this.loggedSkippedRepos.add(event.repo);
265
265
  this.options.logger(
266
- `[agent-serve] cursor-events: skipping events for ${event.repo}: not in this host's subscription (another consumer of this API key likely subscribed it)`
266
+ `[agentkit] cursor-events: skipping events for ${event.repo}: not in this host's subscription (another consumer of this API key likely subscribed it)`
267
267
  );
268
268
  }
269
269
  return false;
@@ -12,6 +12,8 @@ import { randomUUID } from "node:crypto";
12
12
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
13
13
  import { join } from "node:path";
14
14
 
15
+ import { LOG_PREFIX } from "./distribution.js";
16
+
15
17
  const OFFSET_SNAPSHOT_VERSION = 1 as const;
16
18
  const DEFAULT_POLL_WAIT_MS = 25_000;
17
19
  const DEFAULT_POLL_LIMIT = 100;
@@ -98,7 +100,7 @@ interface PollResponse<TEvent> {
98
100
  * anchor/poll/dedupe/offset loop, and backoff — is shared.
99
101
  */
100
102
  export interface RelayAdapter<TEvent extends { offset: string }> {
101
- /** Log fragment, e.g. `cursor-events` → `[agent-serve] cursor-events: …`. */
103
+ /** Log fragment, e.g. `cursor-events` → `[agentkit] cursor-events: …`. */
102
104
  readonly logPrefix: string;
103
105
  /** Subscription registration path, e.g. `/v0/scm-events/subscriptions`. */
104
106
  readonly subscribePath: string;
@@ -231,7 +233,7 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
231
233
 
232
234
  private log(line: string): void {
233
235
  this.options.logger(
234
- `[agent-serve] ${this.options.adapter.logPrefix}: ${line}`
236
+ `${LOG_PREFIX} ${this.options.adapter.logPrefix}: ${line}`
235
237
  );
236
238
  }
237
239
 
@@ -122,7 +122,7 @@ export function createCursorSlackApiCaller(args: {
122
122
  };
123
123
  } catch (error) {
124
124
  args.logger?.(
125
- `[agent-serve] cursor-slack: ${method} proxy call failed: ${describeRelayError(error)}`
125
+ `[agentkit] cursor-slack: ${method} proxy call failed: ${describeRelayError(error)}`
126
126
  );
127
127
  return { ok: false, error: "cursor_backend_unreachable" };
128
128
  }
@@ -27,6 +27,18 @@ export type DeploymentStatus =
27
27
  | "stopping"
28
28
  | "stopped";
29
29
 
30
+ /** Customer-safe deploy progress (no hosting phase / infra names). */
31
+ export type DeploymentProgress =
32
+ | "queued"
33
+ | "preparing"
34
+ | "building"
35
+ | "finalizing"
36
+ | "running"
37
+ | "failed"
38
+ | "stopping"
39
+ | "stopped"
40
+ | "degraded";
41
+
30
42
  export const TERMINAL_DEPLOY_STATUSES: ReadonlySet<DeploymentStatus> = new Set([
31
43
  "running",
32
44
  "degraded",
@@ -118,6 +130,11 @@ export interface Deployment {
118
130
  agentPath?: string;
119
131
  desiredState?: string;
120
132
  status: DeploymentStatus;
133
+ /**
134
+ * Customer-safe deploy progress (no hosting internals). Present on newer
135
+ * backends; older responses omit it.
136
+ */
137
+ progress?: DeploymentProgress;
121
138
  generation: number;
122
139
  observedGeneration?: number | null;
123
140
  engineGeneration?: number | null;
@@ -131,7 +148,8 @@ export interface Deployment {
131
148
  cursorEventRepos?: string[];
132
149
  /** Normalized egress allowlist for the engine pod. */
133
150
  egressAllowedDomains?: string[];
134
- serviceAccountId?: number | null;
151
+ /** Service-account id (`sa_…`); null on legacy rows. */
152
+ serviceAccountId?: string | number | null;
135
153
  /** Masked pod credential; the raw key is never returned. */
136
154
  podCredentialMaskedKey?: string | null;
137
155
  /** Names of the secrets set on the deployment (values never returned). */
@@ -196,6 +214,19 @@ const deploymentSchema = z.object({
196
214
  "stopping",
197
215
  "stopped",
198
216
  ]),
217
+ progress: z
218
+ .enum([
219
+ "queued",
220
+ "preparing",
221
+ "building",
222
+ "finalizing",
223
+ "running",
224
+ "failed",
225
+ "stopping",
226
+ "stopped",
227
+ "degraded",
228
+ ])
229
+ .optional(),
199
230
  generation: z.number(),
200
231
  observedGeneration: z.number().nullable().optional(),
201
232
  engineGeneration: z.number().nullable().optional(),
@@ -207,8 +238,9 @@ const deploymentSchema = z.object({
207
238
  lastReconciledAt: z.string().nullable().optional(),
208
239
  cursorEventRepos: z.array(z.string()).optional(),
209
240
  egressAllowedDomains: z.array(z.string()).optional(),
210
- // Null on legacy rows that predate service-account-backed deploys.
211
- serviceAccountId: z.number().nullable().optional(),
241
+ // String ids (`sa_…`) in prod; older mocks/fixtures used numbers. Null on
242
+ // legacy rows that predate service-account-backed deploys.
243
+ serviceAccountId: z.union([z.string(), z.number()]).nullable().optional(),
212
244
  podCredentialMaskedKey: z.string().nullable().optional(),
213
245
  secretNames: z.array(z.string()).optional(),
214
246
  });
@@ -552,13 +584,22 @@ function mapErrorResponse(
552
584
  `${serverError ?? "Not authorized"} — deploying needs team-admin permission and the cloud-agent entitlement on that team.`,
553
585
  403
554
586
  );
555
- case 404:
587
+ case 404: {
588
+ // Fastify's route-not-found envelope means we hit the wrong host
589
+ // (api2 vs api.cursor.com), not a missing deployment / closed gate.
590
+ if (/Route \S+ not found/i.test(bodyText)) {
591
+ return new DeployApiError(
592
+ `Agent Serve deploy API not found on this host — use the default api.cursor.com (unset CURSOR_API_BASE_URL), or point CURSOR_API_BASE_URL at a backend that mounts /internal/agent-serve/*.`,
593
+ 404
594
+ );
595
+ }
556
596
  return new DeployApiError(
557
597
  context.slug === undefined
558
598
  ? `Not found — the agent_serve_mvp feature gate may not be enabled for your team.`
559
599
  : `Deployment${slugPart} was not found — check the slug and --team, or the agent_serve_mvp feature gate may not be enabled for your team.`,
560
600
  404
561
601
  );
602
+ }
562
603
  case 409:
563
604
  return new DeployApiError(
564
605
  `A concurrent deploy or stop is changing deployment${slugPart}; retry in a moment.`,
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Infer `agentkit deploy` git source fields from a local checkout.
3
+ *
4
+ * From an agent directory inside a git repo with an `origin` remote, the
5
+ * CLI can fill `--repo` / `--ref` / `--path` so `agentkit deploy` (no
6
+ * flags) is enough. Explicit flags always win.
7
+ */
8
+
9
+ import { execFile } from "node:child_process";
10
+ import { relative, resolve, sep } from "node:path";
11
+ import { promisify } from "node:util";
12
+
13
+ const execFileAsync = promisify(execFile);
14
+
15
+ /** Injectable git runner (cwd + args → stdout, or undefined on failure). */
16
+ export type GitRunner = (
17
+ args: readonly string[],
18
+ cwd: string
19
+ ) => Promise<string | undefined>;
20
+
21
+ export interface InferredDeploySource {
22
+ gitRepoUrl?: string;
23
+ gitRef?: string;
24
+ /** Agent directory relative to the repo root (posix). Omitted at root. */
25
+ agentPath?: string;
26
+ }
27
+
28
+ const defaultGitRunner: GitRunner = async (args, cwd) => {
29
+ try {
30
+ const { stdout } = await execFileAsync("git", [...args], { cwd });
31
+ const trimmed = stdout.trim();
32
+ return trimmed === "" ? undefined : trimmed;
33
+ } catch {
34
+ return undefined;
35
+ }
36
+ };
37
+
38
+ /**
39
+ * Normalize a git remote URL to the https form the control plane accepts
40
+ * (`https://host/owner/repo`, no `.git` suffix). Returns undefined for
41
+ * unrecognized shapes.
42
+ */
43
+ export function httpsGitRepoUrlFromRemote(remote: string): string | undefined {
44
+ let url = remote.trim();
45
+ if (url === "") {
46
+ return undefined;
47
+ }
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)}`;
53
+ } else if (!/^https?:\/\//i.test(url)) {
54
+ return undefined;
55
+ }
56
+
57
+ let parsed: URL;
58
+ try {
59
+ parsed = new URL(url);
60
+ } catch {
61
+ return undefined;
62
+ }
63
+ const path = parsed.pathname
64
+ .replace(/\.git$/i, "")
65
+ .replace(/\/+$/, "")
66
+ .replace(/^\/+/, "");
67
+ if (path === "" || !path.includes("/")) {
68
+ return undefined;
69
+ }
70
+ // Use hostname (not host) so ssh://git@github.com:22/… does not keep :22.
71
+ return `https://${parsed.hostname}/${path}`;
72
+ }
73
+
74
+ /**
75
+ * Path of `dir` relative to `gitRoot`, using `/` separators. Undefined when
76
+ * `dir` is outside the root; omitted (undefined) when `dir` *is* the root
77
+ * so the control plane treats the agent as living at the repo root.
78
+ */
79
+ export function agentPathRelativeToGitRoot(
80
+ dir: string,
81
+ gitRoot: string
82
+ ): string | undefined {
83
+ const absDir = resolve(dir);
84
+ const absRoot = resolve(gitRoot);
85
+ const rel = relative(absRoot, absDir);
86
+ if (rel === "") {
87
+ return undefined;
88
+ }
89
+ // Outside the repo (or a Windows drive mismatch) → cannot infer.
90
+ if (rel.startsWith("..") || rel.split(sep).includes("..")) {
91
+ return undefined;
92
+ }
93
+ return rel.split(sep).join("/");
94
+ }
95
+
96
+ /**
97
+ * Probe the git checkout containing `dir` for deploy source fields.
98
+ * Missing pieces stay undefined (static deploy / server defaults).
99
+ */
100
+ export async function inferDeployGitSource(
101
+ dir: string,
102
+ options?: { git?: GitRunner }
103
+ ): Promise<InferredDeploySource> {
104
+ const git = options?.git ?? defaultGitRunner;
105
+ const absDir = resolve(dir);
106
+
107
+ const gitRoot = await git(["rev-parse", "--show-toplevel"], absDir);
108
+ if (gitRoot === undefined) {
109
+ return {};
110
+ }
111
+
112
+ const remote = await git(["remote", "get-url", "origin"], gitRoot);
113
+ const gitRepoUrl =
114
+ remote === undefined ? undefined : httpsGitRepoUrlFromRemote(remote);
115
+
116
+ const branch = await git(["rev-parse", "--abbrev-ref", "HEAD"], gitRoot);
117
+ let gitRef: string | undefined;
118
+ if (branch !== undefined && branch !== "HEAD") {
119
+ gitRef = branch;
120
+ } else {
121
+ // Detached HEAD — pin the exact commit so the control plane still
122
+ // has something concrete to build.
123
+ gitRef = await git(["rev-parse", "HEAD"], gitRoot);
124
+ }
125
+
126
+ const agentPath = agentPathRelativeToGitRoot(absDir, gitRoot);
127
+
128
+ return {
129
+ gitRepoUrl,
130
+ gitRef,
131
+ agentPath,
132
+ };
133
+ }
@@ -61,6 +61,7 @@ import {
61
61
  } from "./hosting.js";
62
62
  import { toolInputJsonSchema } from "./json-schema.js";
63
63
  import { MCP_ENDPOINT_PATH, mcpEndpointToolNames } from "./mcp-endpoint.js";
64
+ import { withIdentityPreamble } from "./workspace.js";
64
65
 
65
66
  const MODULE_EXTENSIONS = new Set([".ts", ".mts", ".js", ".mjs"]);
66
67
  const NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
@@ -105,23 +106,26 @@ export async function isAgentProjectDir(dir: string): Promise<boolean> {
105
106
  return hasFlatLayoutMarkers(resolved);
106
107
  }
107
108
 
109
+ /** One immediate child agent directory under a multi-agent parent. */
110
+ export interface ChildAgentDir {
111
+ slug: string;
112
+ dir: string;
113
+ }
114
+
108
115
  /**
109
- * Discover the agent projects directly under `rootDir` (one level deep):
110
- * each immediate child directory that is itself an agent project becomes a
111
- * mounted agent, keyed by a slug derived from its directory name. Used for
112
- * multi-agent serving, where one server hosts many agents under
113
- * `/<slug>/...`. Throws when no child projects are found.
116
+ * List agent projects directly under `rootDir` (one level deep) without
117
+ * loading them. Empty when the path is missing or has no child agents.
118
+ * Slug rules match {@link loadAgentProjects} / multi-agent serve.
114
119
  */
115
- export async function loadAgentProjects(
116
- rootDir: string,
117
- options: LoadAgentProjectOptions = {}
118
- ): Promise<Array<{ slug: string; project: AgentProject }>> {
120
+ export async function listChildAgentDirs(
121
+ rootDir: string
122
+ ): Promise<ChildAgentDir[]> {
119
123
  const resolved = resolve(rootDir);
120
124
  if (!(await isDirectory(resolved))) {
121
- throw new Error(`Agent projects directory does not exist: ${resolved}`);
125
+ return [];
122
126
  }
123
127
 
124
- const results: Array<{ slug: string; project: AgentProject }> = [];
128
+ const results: ChildAgentDir[] = [];
125
129
  const seenSlugs = new Set<string>();
126
130
  for (const entry of await sortedEntries(resolved)) {
127
131
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
@@ -139,14 +143,40 @@ export async function loadAgentProjects(
139
143
  throw new Error(`Duplicate agent slug "${slug}" under ${resolved}.`);
140
144
  }
141
145
  seenSlugs.add(slug);
142
- results.push({ slug, project: await loadAgentProject(childDir, options) });
146
+ results.push({ slug, dir: childDir });
147
+ }
148
+ return results;
149
+ }
150
+
151
+ /**
152
+ * Discover the agent projects directly under `rootDir` (one level deep):
153
+ * each immediate child directory that is itself an agent project becomes a
154
+ * mounted agent, keyed by a slug derived from its directory name. Used for
155
+ * multi-agent serving, where one server hosts many agents under
156
+ * `/<slug>/...`. Throws when no child projects are found.
157
+ */
158
+ export async function loadAgentProjects(
159
+ rootDir: string,
160
+ options: LoadAgentProjectOptions = {}
161
+ ): Promise<Array<{ slug: string; project: AgentProject }>> {
162
+ const resolved = resolve(rootDir);
163
+ if (!(await isDirectory(resolved))) {
164
+ throw new Error(`Agent projects directory does not exist: ${resolved}`);
143
165
  }
144
166
 
145
- if (results.length === 0) {
167
+ const children = await listChildAgentDirs(resolved);
168
+ if (children.length === 0) {
146
169
  throw new Error(
147
170
  `No agent projects found directly under ${resolved}. Each agent should be a subdirectory with its own agent/ directory (or flat-layout markers).`
148
171
  );
149
172
  }
173
+ const results: Array<{ slug: string; project: AgentProject }> = [];
174
+ for (const child of children) {
175
+ results.push({
176
+ slug: child.slug,
177
+ project: await loadAgentProject(child.dir, options),
178
+ });
179
+ }
150
180
  return results;
151
181
  }
152
182
 
@@ -287,12 +317,19 @@ async function loadAgent(
287
317
  options: { name: string; isRoot: boolean }
288
318
  ): Promise<ResolvedAgent> {
289
319
  const config = await loadAgentConfig(ctx, agentDir, options.isRoot);
290
- const instructions = await loadInstructions(
320
+ const name = config?.name ?? options.name;
321
+ const authoredInstructions = await loadInstructions(
291
322
  ctx,
292
323
  agentDir,
293
324
  config,
294
325
  options.isRoot
295
326
  );
327
+ // Root agents without authored instructions stay undefined (diagnostic
328
+ // already recorded). Subagents still get the identity line alone.
329
+ const instructions =
330
+ authoredInstructions === undefined && options.isRoot
331
+ ? undefined
332
+ : withIdentityPreamble(name, authoredInstructions);
296
333
 
297
334
  // Subagents run as Cursor SDK custom subagents (prompt + description +
298
335
  // model); their other slots are inert today, so don't load them — just
@@ -300,7 +337,7 @@ async function loadAgent(
300
337
  if (!options.isRoot) {
301
338
  await warnUnsupportedSubagentSlots(ctx, agentDir);
302
339
  return {
303
- name: config?.name ?? options.name,
340
+ name,
304
341
  ...(config?.description === undefined
305
342
  ? {}
306
343
  : { description: config.description }),
@@ -347,7 +384,7 @@ async function loadAgent(
347
384
  }
348
385
 
349
386
  return {
350
- name: config?.name ?? options.name,
387
+ name,
351
388
  description: config?.description,
352
389
  // Root agents always get a concrete model: authored value or DEFAULT_MODEL.
353
390
  model: isValidModelSetting(config?.model) ? config.model : DEFAULT_MODEL,
@@ -25,6 +25,14 @@ export const LEGACY_PACKAGE_NAME = "@anysphere/agent-serve";
25
25
  */
26
26
  export const AGENTKIT_PACKAGE_NAME = "@cursor/agentkit";
27
27
 
28
+ /**
29
+ * Bracketed product tag for stderr / ServeOptions.logger lines
30
+ * (`[agentkit] …`, `[agentkit/slack] …`).
31
+ */
32
+ export const LOG_PRODUCT = "agentkit";
33
+ export const LOG_PREFIX: string = `[${LOG_PRODUCT}]`;
34
+ export const SLACK_LOG_PREFIX: string = `[${LOG_PRODUCT}/slack]`;
35
+
28
36
  /**
29
37
  * Command name this CLI was invoked as. Both bins point at the same entry;
30
38
  * keep help text and hints copy-pasteable for whichever the user typed.
@@ -109,11 +109,11 @@ export async function ensureDocsSiteBundle(
109
109
  const toolchain = resolveDocsToolchain();
110
110
  if (toolchain === undefined) {
111
111
  logger(
112
- "[agent-serve] docs site missing and vitepress toolchain unavailable; /docs will show a build hint"
112
+ "[agentkit] docs site missing and vitepress toolchain unavailable; /docs will show a build hint"
113
113
  );
114
114
  return;
115
115
  }
116
- logger("[agent-serve] building docs site (vitepress)...");
116
+ logger("[agentkit] building docs site (vitepress)...");
117
117
  const code = await new Promise<number>((resolve, reject) => {
118
118
  const child = spawn(
119
119
  process.execPath,
@@ -131,7 +131,7 @@ export async function ensureDocsSiteBundle(
131
131
  "docs vitepress build finished but dist/docs/index.html is missing"
132
132
  );
133
133
  }
134
- logger("[agent-serve] docs site build complete");
134
+ logger("[agentkit] docs site build complete");
135
135
  }
136
136
 
137
137
  interface DocsToolchain {