@cursor/july 0.1.61 → 0.1.68

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 (196) hide show
  1. package/dist/bin/agent-serve.js +8 -0
  2. package/dist/channels/github/github-channel.d.ts +1 -1
  3. package/dist/channels/github/github-channel.d.ts.map +1 -1
  4. package/dist/channels/github/github-channel.js +14 -1
  5. package/dist/channels/github/types.d.ts +2 -0
  6. package/dist/channels/github/types.d.ts.map +1 -1
  7. package/dist/channels/slack/channel-watch.d.ts +2 -1
  8. package/dist/channels/slack/channel-watch.d.ts.map +1 -1
  9. package/dist/channels/slack/channel-watch.js +7 -3
  10. package/dist/channels/slack/dispatch.d.ts +9 -0
  11. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  12. package/dist/channels/slack/dispatch.js +41 -21
  13. package/dist/channels/slack/manifest.d.ts.map +1 -1
  14. package/dist/channels/slack/manifest.js +1 -0
  15. package/dist/channels/slack/nudge-store.d.ts +16 -0
  16. package/dist/channels/slack/nudge-store.d.ts.map +1 -1
  17. package/dist/channels/slack/nudge-store.js +53 -0
  18. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  19. package/dist/channels/slack/slack-channel.js +4 -1
  20. package/dist/channels/slack/types.d.ts +22 -5
  21. package/dist/channels/slack/types.d.ts.map +1 -1
  22. package/dist/docs/404.html +2 -2
  23. package/dist/docs/ab.html +3 -3
  24. package/dist/docs/assets/{app.CW3KJh72.js → app.C5XUTCw9.js} +1 -1
  25. package/dist/docs/assets/chunks/@localSearchIndexroot.DvUR3lv9.js +1 -0
  26. package/dist/docs/assets/chunks/{VPLocalSearchBox.Bk6EWpPj.js → VPLocalSearchBox.CGDTAYHY.js} +1 -1
  27. package/dist/docs/assets/chunks/{theme.Bz9AodBA.js → theme.lPixh250.js} +2 -2
  28. package/dist/docs/assets/{reference_connections.md.CSQiNzre.js → reference_connections.md.Bwswbm6H.js} +13 -3
  29. package/dist/docs/assets/{reference_connections.md.CSQiNzre.lean.js → reference_connections.md.Bwswbm6H.lean.js} +1 -1
  30. package/dist/docs/assets/{reference_tools.md.kJfJAP2m.js → reference_tools.md.Wmvb7Lob.js} +2 -2
  31. package/dist/docs/assets/{reference_tools.md.kJfJAP2m.lean.js → reference_tools.md.Wmvb7Lob.lean.js} +1 -1
  32. package/dist/docs/building-with-agents.html +3 -3
  33. package/dist/docs/concepts.html +3 -3
  34. package/dist/docs/deployment.html +3 -3
  35. package/dist/docs/evals.html +3 -3
  36. package/dist/docs/example-agents/approval-buddy.html +3 -3
  37. package/dist/docs/example-agents/benny.html +3 -3
  38. package/dist/docs/example-agents/bugbot.html +3 -3
  39. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  40. package/dist/docs/example-agents/codeowners-review.html +3 -3
  41. package/dist/docs/example-agents/concierge.html +3 -3
  42. package/dist/docs/example-agents/fsd.html +3 -3
  43. package/dist/docs/example-agents/index.html +3 -3
  44. package/dist/docs/example-agents/knowledge-base.html +3 -3
  45. package/dist/docs/example-agents/oncall.html +3 -3
  46. package/dist/docs/example-agents/security-reviewer.html +3 -3
  47. package/dist/docs/example-agents/slack-agent.html +3 -3
  48. package/dist/docs/example-agents/weather-agent.html +3 -3
  49. package/dist/docs/guides/agent-to-agent.html +3 -3
  50. package/dist/docs/guides/cloud-runtime.html +3 -3
  51. package/dist/docs/guides/convert-automation.html +3 -3
  52. package/dist/docs/guides/github.html +3 -3
  53. package/dist/docs/guides/human-in-the-loop.html +3 -3
  54. package/dist/docs/guides/mcp-oauth.html +3 -3
  55. package/dist/docs/guides/opentelemetry.html +3 -3
  56. package/dist/docs/guides/slack.html +3 -3
  57. package/dist/docs/guides/webhooks.html +3 -3
  58. package/dist/docs/hashmap.json +1 -1
  59. package/dist/docs/hillclimbing.html +3 -3
  60. package/dist/docs/index.html +3 -3
  61. package/dist/docs/quickstart.html +3 -3
  62. package/dist/docs/reference/agent-config.html +3 -3
  63. package/dist/docs/reference/artifacts.html +3 -3
  64. package/dist/docs/reference/channels.html +3 -3
  65. package/dist/docs/reference/cli.html +3 -3
  66. package/dist/docs/reference/connections.html +15 -5
  67. package/dist/docs/reference/hooks.html +3 -3
  68. package/dist/docs/reference/http-api.html +3 -3
  69. package/dist/docs/reference/instructions.html +3 -3
  70. package/dist/docs/reference/playground.html +3 -3
  71. package/dist/docs/reference/project-layout.html +3 -3
  72. package/dist/docs/reference/prompt.html +3 -3
  73. package/dist/docs/reference/schedules.html +3 -3
  74. package/dist/docs/reference/sessions.html +3 -3
  75. package/dist/docs/reference/skills.html +3 -3
  76. package/dist/docs/reference/subagents.html +3 -3
  77. package/dist/docs/reference/tools.html +5 -5
  78. package/dist/docs/scaffolding-agents.html +3 -3
  79. package/dist/docs/storage.html +3 -3
  80. package/dist/docs/templates/agentic-owners.html +3 -3
  81. package/dist/docs/templates/demo.html +3 -3
  82. package/dist/docs/templates/pr-autofixer.html +3 -3
  83. package/dist/docs/templates/security-reviewer.html +3 -3
  84. package/dist/docs/templates/triage.html +3 -3
  85. package/dist/docs/troubleshooting.html +3 -3
  86. package/dist/files-backends/agent-store-presigned-url.js +1 -1
  87. package/dist/index.d.ts +2 -2
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +1 -1
  90. package/dist/internal/advertise-tools.d.ts +74 -0
  91. package/dist/internal/advertise-tools.d.ts.map +1 -0
  92. package/dist/internal/advertise-tools.js +152 -0
  93. package/dist/internal/cli-deploy.d.ts.map +1 -1
  94. package/dist/internal/cli-deploy.js +11 -3
  95. package/dist/internal/cli-manifest.d.ts +19 -0
  96. package/dist/internal/cli-manifest.d.ts.map +1 -0
  97. package/dist/internal/cli-manifest.js +49 -0
  98. package/dist/internal/cli-slack.d.ts.map +1 -1
  99. package/dist/internal/cli-slack.js +9 -1
  100. package/dist/internal/cursor-agent-template.d.ts +9 -0
  101. package/dist/internal/cursor-agent-template.d.ts.map +1 -0
  102. package/dist/internal/cursor-agent-template.js +37 -0
  103. package/dist/internal/cursor-event-relay.d.ts +5 -1
  104. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  105. package/dist/internal/cursor-event-relay.js +5 -0
  106. package/dist/internal/deploy-client.d.ts +8 -0
  107. package/dist/internal/deploy-client.d.ts.map +1 -1
  108. package/dist/internal/deploy-client.js +21 -3
  109. package/dist/internal/deploy-manifest.d.ts +85 -0
  110. package/dist/internal/deploy-manifest.d.ts.map +1 -0
  111. package/dist/internal/deploy-manifest.js +106 -0
  112. package/dist/internal/deployment-scm-event-stream.d.ts +113 -0
  113. package/dist/internal/deployment-scm-event-stream.d.ts.map +1 -0
  114. package/dist/internal/deployment-scm-event-stream.js +460 -0
  115. package/dist/internal/discovery.d.ts.map +1 -1
  116. package/dist/internal/discovery.js +69 -0
  117. package/dist/internal/init-project.d.ts.map +1 -1
  118. package/dist/internal/init-project.js +5 -17
  119. package/dist/internal/mcp-host.d.ts +18 -0
  120. package/dist/internal/mcp-host.d.ts.map +1 -1
  121. package/dist/internal/mcp-host.js +41 -5
  122. package/dist/internal/mcp-oauth.d.ts +0 -4
  123. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  124. package/dist/internal/mcp-oauth.js +0 -4
  125. package/dist/internal/sdk-runner.d.ts +1 -1
  126. package/dist/internal/sdk-runner.d.ts.map +1 -1
  127. package/dist/internal/sdk-runner.js +36 -13
  128. package/dist/internal/server.d.ts.map +1 -1
  129. package/dist/internal/server.js +20 -2
  130. package/dist/internal/session-engine.d.ts +66 -0
  131. package/dist/internal/session-engine.d.ts.map +1 -1
  132. package/dist/internal/session-engine.js +430 -110
  133. package/dist/internal/shallow-clone.d.ts +115 -0
  134. package/dist/internal/shallow-clone.d.ts.map +1 -0
  135. package/dist/internal/shallow-clone.js +292 -0
  136. package/dist/internal/slack-provision-client.d.ts +2 -0
  137. package/dist/internal/slack-provision-client.d.ts.map +1 -1
  138. package/dist/internal/slack-provision-client.js +1 -0
  139. package/dist/internal/turn-lease-store.d.ts +44 -0
  140. package/dist/internal/turn-lease-store.d.ts.map +1 -0
  141. package/dist/internal/turn-lease-store.js +139 -0
  142. package/dist/internal/workspace.d.ts +12 -0
  143. package/dist/internal/workspace.d.ts.map +1 -1
  144. package/dist/internal/workspace.js +28 -0
  145. package/dist/playground/assets/index-CoL7xCJl.css +1 -0
  146. package/dist/playground/assets/index-yxs1heea.js +85 -0
  147. package/dist/playground/index.html +2 -2
  148. package/dist/storage-protocol.d.ts +122 -0
  149. package/dist/storage-protocol.d.ts.map +1 -0
  150. package/dist/storage-protocol.js +173 -0
  151. package/dist/storage.d.ts +2 -72
  152. package/dist/storage.d.ts.map +1 -1
  153. package/dist/storage.js +2 -82
  154. package/dist/types.d.ts +114 -4
  155. package/dist/types.d.ts.map +1 -1
  156. package/docs/reference/connections.md +80 -0
  157. package/docs/reference/tools.md +14 -0
  158. package/package.json +4 -2
  159. package/skills/github/SKILL.md +2 -1
  160. package/src/bin/agent-serve.ts +9 -0
  161. package/src/channels/github/github-channel.ts +18 -2
  162. package/src/channels/github/types.ts +2 -0
  163. package/src/channels/slack/channel-watch.ts +6 -3
  164. package/src/channels/slack/dispatch.ts +40 -3
  165. package/src/channels/slack/manifest.ts +1 -0
  166. package/src/channels/slack/nudge-store.ts +56 -0
  167. package/src/channels/slack/slack-channel.ts +3 -0
  168. package/src/channels/slack/types.ts +22 -5
  169. package/src/index.ts +5 -0
  170. package/src/internal/advertise-tools.ts +229 -0
  171. package/src/internal/cli-deploy.ts +22 -1
  172. package/src/internal/cli-manifest.ts +63 -0
  173. package/src/internal/cli-slack.ts +10 -1
  174. package/src/internal/cursor-agent-template.ts +46 -0
  175. package/src/internal/cursor-event-relay.ts +8 -1
  176. package/src/internal/deploy-client.ts +35 -6
  177. package/src/internal/deploy-manifest.ts +167 -0
  178. package/src/internal/deployment-scm-event-stream.ts +577 -0
  179. package/src/internal/discovery.ts +85 -0
  180. package/src/internal/init-project.ts +11 -20
  181. package/src/internal/mcp-host.ts +66 -9
  182. package/src/internal/mcp-oauth.ts +0 -4
  183. package/src/internal/sdk-runner.ts +50 -11
  184. package/src/internal/server.ts +21 -2
  185. package/src/internal/session-engine.ts +385 -19
  186. package/src/internal/shallow-clone.ts +407 -0
  187. package/src/internal/slack-provision-client.ts +3 -0
  188. package/src/internal/turn-lease-store.ts +147 -0
  189. package/src/internal/workspace.ts +27 -0
  190. package/src/storage-protocol.ts +247 -0
  191. package/src/storage.ts +16 -110
  192. package/src/types.ts +118 -4
  193. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +4 -1
  194. package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +0 -1
  195. package/dist/playground/assets/index-ClbuaLpm.css +0 -1
  196. package/dist/playground/assets/index-SqkR13k_.js +0 -85
@@ -22,6 +22,7 @@ import {
22
22
  SIGN_IN_HINT,
23
23
  } from "./cursor/credentials.js";
24
24
  import { authorizeCursorAccountConnectors } from "./cursor-account-mcp-auth.js";
25
+ import { readDeployTemplate } from "./cursor-agent-template.js";
25
26
  import {
26
27
  AgentServeDeployClient,
27
28
  type DeleteAccepted,
@@ -38,6 +39,10 @@ import {
38
39
  TERMINAL_DEPLOY_STATUSES,
39
40
  validateDeploymentSlug,
40
41
  } from "./deploy-client.js";
42
+ import {
43
+ type AgentDeployManifestV1,
44
+ buildAgentDeployManifest,
45
+ } from "./deploy-manifest.js";
41
46
  import { type GitRunner, inferDeployGitSource } from "./deploy-source.js";
42
47
  import {
43
48
  type ChildAgentDir,
@@ -661,11 +666,19 @@ async function deployOne(
661
666
  ...(egressAllowedDomains.length === 0 ? {} : { egressAllowedDomains }),
662
667
  publicRoutePaths:
663
668
  publicRoutePaths.length === 0 ? undefined : publicRoutePaths,
669
+ // Offline metadata extraction (Wd): the CP persists this before any VM
670
+ // starts. Directories without an agent project send no manifest.
671
+ ...(manifest === undefined ? {} : { manifest: manifest.deployManifest }),
664
672
  };
665
673
 
666
674
  let accepted: DeployAccepted;
667
675
  try {
668
- accepted = await ctx.client.deploy({ teamId: ctx.teamId, slug, source });
676
+ accepted = await ctx.client.deploy({
677
+ teamId: ctx.teamId,
678
+ slug,
679
+ source,
680
+ template: readDeployTemplate(dir),
681
+ });
669
682
  } catch (error) {
670
683
  return renderApiError(error, ctx.err);
671
684
  }
@@ -851,6 +864,13 @@ interface DeployManifest {
851
864
  * (only `/v1/channels/…` subtrees can be public); surfaced as a warning.
852
865
  */
853
866
  httpChannelPublicEndpoint: boolean;
867
+ /**
868
+ * The offline deploy manifest (Wd): derived event declarations, channel
869
+ * kinds, egress, routes, and the full `/v1/info` payload. Attached to the
870
+ * deploy request so the control plane stores metadata before any VM
871
+ * starts; servers that predate manifest storage strip the field.
872
+ */
873
+ deployManifest: AgentDeployManifestV1;
854
874
  }
855
875
 
856
876
  /**
@@ -875,6 +895,7 @@ async function readDeployManifest(dir: string): Promise<DeployManifest> {
875
895
  httpChannelPublicEndpoint: (project.httpChannel.auth ?? []).some(
876
896
  isPublicEndpointPolicy
877
897
  ),
898
+ deployManifest: buildAgentDeployManifest(project),
878
899
  };
879
900
  }
880
901
 
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `july manifest` — print the offline deploy manifest (Wd-1 of
3
+ * `docs/agent-serve/agent-sdk-event-ingress-plan.md`).
4
+ *
5
+ * Loads the agent project from disk and emits the statically-derived
6
+ * metadata the control plane stores at deploy time: declared GitHub webhook
7
+ * events, egress domains, public routes, and the full `/v1/info` payload.
8
+ * Runs entirely offline — no VM, no engine, no network.
9
+ */
10
+
11
+ import {
12
+ buildAgentDeployManifest,
13
+ parseAgentDeployManifest,
14
+ } from "./deploy-manifest.js";
15
+ import { isAgentProjectDir, loadAgentProject } from "./discovery.js";
16
+
17
+ export interface CmdManifestOptions {
18
+ dir: string;
19
+ /** Compact single-line JSON (machine consumption); default pretty. */
20
+ json?: boolean;
21
+ /** Test seams. */
22
+ out?: (line: string) => void;
23
+ err?: (line: string) => void;
24
+ }
25
+
26
+ export async function cmdManifest(
27
+ options: CmdManifestOptions
28
+ ): Promise<number> {
29
+ const out = options.out ?? ((line: string) => process.stdout.write(line));
30
+ const err = options.err ?? ((line: string) => process.stderr.write(line));
31
+
32
+ if (!(await isAgentProjectDir(options.dir))) {
33
+ err(
34
+ `No agent project found at ${options.dir} (expected an agent.ts default-exporting defineAgent({ ... })).\n`
35
+ );
36
+ return 1;
37
+ }
38
+
39
+ const project = await loadAgentProject(options.dir);
40
+ for (const diagnostic of project.diagnostics) {
41
+ err(`${diagnostic.severity}: ${diagnostic.message}\n`);
42
+ }
43
+ if (project.diagnostics.some((d) => d.severity === "error")) {
44
+ return 1;
45
+ }
46
+
47
+ const manifest = buildAgentDeployManifest(project);
48
+ // Round-trip through the wire schema so `july manifest` can never emit
49
+ // something the control plane contract would reject.
50
+ if (
51
+ parseAgentDeployManifest(JSON.parse(JSON.stringify(manifest))) === undefined
52
+ ) {
53
+ err("internal error: generated manifest failed schema validation\n");
54
+ return 1;
55
+ }
56
+
57
+ out(
58
+ options.json === true
59
+ ? `${JSON.stringify(manifest)}\n`
60
+ : `${JSON.stringify(manifest, null, 2)}\n`
61
+ );
62
+ return 0;
63
+ }
@@ -402,7 +402,16 @@ function writePendingAdminWait(
402
402
  ? poll.oauthAuthorizeUrl
403
403
  : undefined;
404
404
  ctx.err(`Waiting for a Slack admin to approve the app${appSuffix}.\n\n`);
405
- if (requestUrl !== undefined) {
405
+ if (poll.approvalRequest === "pending") {
406
+ // Already in the admin queue: nothing to file, so no browser hop.
407
+ ctx.err(
408
+ ctx.dim(` Approval request sent; workspace owners were notified.\n\n`)
409
+ );
410
+ if (requestUrl !== undefined) {
411
+ ctx.err(ctx.dim(` Send a reminder from:\n\n`));
412
+ ctx.err(` ${requestUrl}\n\n`);
413
+ }
414
+ } else if (requestUrl !== undefined) {
406
415
  ctx.err(
407
416
  ctx.dim(
408
417
  ` Slack will notify workspace owners once the request is in their queue.\n` +
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ const MAX = 64;
5
+ const TEMPLATE_NAMES = [
6
+ "agentic-owners",
7
+ "demo",
8
+ "pr-autofixer",
9
+ "security-reviewer",
10
+ "triage-linear",
11
+ "triage-jira",
12
+ ] as const;
13
+ export type TemplateName = (typeof TEMPLATE_NAMES)[number];
14
+ const NAMES = new Set<string>(TEMPLATE_NAMES);
15
+
16
+ export function canonicalTemplateName(name: string): string {
17
+ return name === "triage" ? "triage-linear" : name;
18
+ }
19
+
20
+ export function isTemplateName(name: string): name is TemplateName {
21
+ return NAMES.has(name);
22
+ }
23
+
24
+ /** Raw scaffold marker. Validation depends on the consuming boundary. */
25
+ export function readCursorAgentTemplate(rootDir: string): string | undefined {
26
+ try {
27
+ const pkg = JSON.parse(
28
+ readFileSync(join(rootDir, "package.json"), "utf8")
29
+ ) as {
30
+ cursorAgentTemplate?: unknown;
31
+ };
32
+ const stamp = pkg.cursorAgentTemplate;
33
+ return typeof stamp === "string" ? stamp : undefined;
34
+ } catch {
35
+ return undefined;
36
+ }
37
+ }
38
+
39
+ export function readDeployTemplate(rootDir: string): string | undefined {
40
+ const stamp = readCursorAgentTemplate(rootDir);
41
+ if (stamp === undefined || stamp.length === 0 || stamp.length > MAX) {
42
+ return undefined;
43
+ }
44
+ const name = canonicalTemplateName(stamp);
45
+ return isTemplateName(name) ? name : undefined;
46
+ }
@@ -91,7 +91,7 @@ export type CursorScmWorkflowRunMeta = CursorScmCiBaseMeta & {
91
91
  workflowId?: number;
92
92
  };
93
93
 
94
- /** One event delivered by `GET /v0/scm-events` (metadata only). */
94
+ /** One event delivered by `GET /v0/scm-events`. */
95
95
  export interface CursorScmEvent {
96
96
  offset: string;
97
97
  provider: string;
@@ -99,6 +99,8 @@ export interface CursorScmEvent {
99
99
  deliveryId?: string;
100
100
  repo: string;
101
101
  receivedAtMs: number;
102
+ /** Verified webhook body when the API stored it. */
103
+ payload?: unknown;
102
104
  /** Webhook `action` when present. */
103
105
  action?: string;
104
106
  /** PR number when the delivery is PR-scoped. */
@@ -235,6 +237,11 @@ function readDispatchError(body: unknown): string | undefined {
235
237
  return typeof error === "string" && error !== "" ? error : undefined;
236
238
  }
237
239
 
240
+ /** Stored webhook body, or a metadata stub for older stream entries. */
241
+ export function scmEventToGitHubPayload(event: CursorScmEvent): unknown {
242
+ return event.payload ?? scmMetadataToGitHubPayload(event);
243
+ }
244
+
238
245
  /**
239
246
  * Minimal GitHub webhook body from an SCM-events metadata envelope.
240
247
  * When `pullRequest` has title + head/base refs+shas, emits a Zod-valid
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import { z } from "zod";
14
+ import type { AgentDeployManifestV1 } from "./deploy-manifest.js";
14
15
  import {
15
16
  MAX_SECRET_VALUE_BYTES,
16
17
  MAX_SECRETS,
@@ -74,6 +75,12 @@ export type DeploySource = {
74
75
  * `X-Agent-Alias-Token` (channels authored with `publicEndpoint()`).
75
76
  */
76
77
  publicRoutePaths?: string[];
78
+ /**
79
+ * Offline deploy manifest (Wd): derived declarations + `/v1/info`
80
+ * payload, extracted at deploy time with no VM. Optional — servers that
81
+ * predate manifest storage strip unknown fields.
82
+ */
83
+ manifest?: AgentDeployManifestV1;
77
84
  };
78
85
 
79
86
  const SLUG_PATTERN = /^[a-z0-9_-]{1,64}$/;
@@ -422,6 +429,7 @@ export class AgentServeDeployClient {
422
429
  teamId: number;
423
430
  slug: string;
424
431
  source: DeploySource;
432
+ template?: string;
425
433
  }): Promise<DeployAccepted> {
426
434
  const slugError = validateDeploymentSlug(args.slug);
427
435
  if (slugError !== undefined) {
@@ -453,13 +461,34 @@ export class AgentServeDeployClient {
453
461
  : { egressAllowedDomains: args.source.egressAllowedDomains }),
454
462
  // JSON.stringify drops undefined keys.
455
463
  publicRoutePaths: args.source.publicRoutePaths,
464
+ ...(args.source.manifest === undefined
465
+ ? {}
466
+ : { manifest: args.source.manifest }),
467
+ template: args.template,
456
468
  };
457
- const raw = await this.request({
458
- method: "POST",
459
- path: "/internal/agent-serve/deployments",
460
- body,
461
- context: { verb: "deploy", slug: args.slug },
462
- });
469
+ const send = (): Promise<unknown> =>
470
+ this.request({
471
+ method: "POST",
472
+ path: "/internal/agent-serve/deployments",
473
+ body,
474
+ context: { verb: "deploy", slug: args.slug },
475
+ });
476
+ let raw: unknown;
477
+ try {
478
+ raw = await send();
479
+ } catch (error) {
480
+ const unsupportedTemplate =
481
+ args.template !== undefined &&
482
+ error instanceof DeployApiError &&
483
+ error.status === 400 &&
484
+ error.message.toLowerCase().includes("template") &&
485
+ /unrecognized|unknown/i.test(error.message);
486
+ if (!unsupportedTemplate) {
487
+ throw error;
488
+ }
489
+ delete body.template;
490
+ raw = await send();
491
+ }
463
492
  return deployAcceptedSchema.parse(raw);
464
493
  }
465
494
 
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Offline deploy manifest (workstream Wd of
3
+ * `docs/agent-serve/agent-sdk-event-ingress-plan.md`, Wd-0/Wd-1).
4
+ *
5
+ * The manifest is every piece of deployment metadata that is statically
6
+ * derivable from the agent project — extracted at build/deploy time, with no
7
+ * VM, no engine, and no network — so the control plane can store it before
8
+ * any pod starts and keep answering metadata questions while a deployment is
9
+ * offline or hibernated.
10
+ *
11
+ * Two layers with different stability contracts:
12
+ *
13
+ * - `declarations` is the **versioned wire contract** consumed by the
14
+ * control plane (Wd-2): GitHub webhook events the mounted channels can
15
+ * dispatch on, egress domains, and public route paths. Validated by
16
+ * {@link agentDeployManifestV1Schema}; the CP stores these as columns /
17
+ * column views. Repos are deliberately NOT here — which repos a
18
+ * deployment watches is per-instance deploy configuration, not a property
19
+ * of the code.
20
+ * - `info` is the full static project description — the same payload the
21
+ * engine serves at `GET /v1/info` (`projectInfo`). Stored opaquely by the
22
+ * CP so `/v1/info`-class reads can be served for offline deployments
23
+ * (Wd-5). Its shape evolves with the SDK and is intentionally not pinned
24
+ * by the wire schema beyond "is an object".
25
+ *
26
+ * Event derivation note: each mounted `githubChannel()` publishes its
27
+ * derived event set on `ChannelMeta.githubWebhookEvents` (computed by
28
+ * `deriveGitHubWebhookEvents` from the declared hooks). The manifest is the
29
+ * union across mounted channels; a project with no GitHub channel gets no
30
+ * `githubWebhookEvents` key at all — such a deployment subscribes to
31
+ * nothing, regardless of the repos configured at deploy time.
32
+ */
33
+
34
+ import { z } from "zod";
35
+ import { isPublicEndpointPolicy } from "../channels.js";
36
+ import { isCursorHostedStorage } from "../storage-backends/cursor-hosted.js";
37
+ import type { AgentProject, AgentProjectInfo } from "../types.js";
38
+ import { projectInfo } from "./discovery.js";
39
+ import { packageVersion } from "./distribution.js";
40
+
41
+ export const DEPLOY_MANIFEST_VERSION: 1 = 1;
42
+
43
+ /**
44
+ * Versioned wire contract for the CP-consumed declaration fields. Kept
45
+ * module-private (the package builds with isolated declarations); consumers
46
+ * use {@link parseAgentDeployManifest} and the explicit interfaces below.
47
+ */
48
+ const agentDeployManifestV1Schema = z.object({
49
+ manifestVersion: z.literal(DEPLOY_MANIFEST_VERSION),
50
+ generatedAtMs: z.number().int().nonnegative(),
51
+ cliVersion: z.string().min(1),
52
+ declarations: z.object({
53
+ githubWebhookEvents: z.array(z.string().min(1).max(64)).max(64).optional(),
54
+ egressDomains: z.array(z.string().min(1)).max(64),
55
+ publicRoutePaths: z.array(z.string().min(1)).max(64),
56
+ frameworkStorage: z.enum(["cursor-hosted", "other", "none"]).optional(),
57
+ channels: z
58
+ .array(
59
+ z.object({
60
+ id: z.string().min(1).max(128),
61
+ kind: z.string().min(1).max(32).optional(),
62
+ })
63
+ )
64
+ .max(64),
65
+ }),
66
+ // Full `projectInfo` payload — opaque to the wire contract.
67
+ info: z.record(z.unknown()),
68
+ });
69
+
70
+ export interface AgentDeployManifestDeclarations {
71
+ /**
72
+ * Union of every mounted GitHub channel's derived webhook events.
73
+ * Absent (not empty) when the project mounts no GitHub channel.
74
+ */
75
+ githubWebhookEvents?: string[];
76
+ egressDomains: string[];
77
+ publicRoutePaths: string[];
78
+ /** Where this runtime mirrors framework-owned sessions and event chunks. */
79
+ frameworkStorage?: "cursor-hosted" | "other" | "none";
80
+ /**
81
+ * Mounted channels with their kinds. The control plane derives the
82
+ * continuation identity (session key) for **defined** kinds it knows
83
+ * ("github", "slack"); a custom channel with no kind gets per-deployment
84
+ * serialization until a declared continuation config exists (a future
85
+ * schema field — reserved, deliberately not implemented yet). This is
86
+ * what lets the CP compute workflow/session identity before any engine
87
+ * runs.
88
+ */
89
+ channels: Array<{ id: string; kind?: string }>;
90
+ }
91
+
92
+ export interface AgentDeployManifestV1 {
93
+ manifestVersion: 1;
94
+ /** Unix ms at extraction. */
95
+ generatedAtMs: number;
96
+ /** `@cursor/july` version that produced the manifest. */
97
+ cliVersion: string;
98
+ declarations: AgentDeployManifestDeclarations;
99
+ /**
100
+ * Full `projectInfo` payload (see `GET /v1/info`). Opaque to the wire
101
+ * contract; the CP stores it verbatim.
102
+ */
103
+ info: AgentProjectInfo;
104
+ }
105
+
106
+ /**
107
+ * Extract the offline deploy manifest from a loaded project. Pure: no
108
+ * network, no filesystem beyond what `loadAgentProject` already read.
109
+ */
110
+ export function buildAgentDeployManifest(
111
+ project: AgentProject,
112
+ options?: { nowMs?: number; cliVersion?: string }
113
+ ): AgentDeployManifestV1 {
114
+ const events = new Set<string>();
115
+ for (const channel of project.channels) {
116
+ for (const event of channel.definition.meta?.githubWebhookEvents ?? []) {
117
+ events.add(event);
118
+ }
119
+ }
120
+
121
+ // Same mapping as `july deploy` (cli-deploy.ts): channels declared public
122
+ // via `publicEndpoint()` become alias-token-free route prefixes.
123
+ const publicRoutePaths = project.channels
124
+ .filter((channel) =>
125
+ (channel.definition.auth ?? []).some(isPublicEndpointPolicy)
126
+ )
127
+ .map((channel) => `/v1/channels/${channel.id}`);
128
+
129
+ return {
130
+ manifestVersion: DEPLOY_MANIFEST_VERSION,
131
+ generatedAtMs: options?.nowMs ?? Date.now(),
132
+ cliVersion: options?.cliVersion ?? packageVersion(),
133
+ declarations: {
134
+ ...(events.size === 0 ? {} : { githubWebhookEvents: [...events].sort() }),
135
+ egressDomains: [...(project.agent.hosting?.egressDomains ?? [])],
136
+ publicRoutePaths,
137
+ frameworkStorage:
138
+ project.storage === undefined
139
+ ? "none"
140
+ : isCursorHostedStorage(project.storage)
141
+ ? "cursor-hosted"
142
+ : "other",
143
+ channels: project.channels.map((channel) => ({
144
+ id: channel.id,
145
+ ...(channel.definition.meta?.kind === undefined
146
+ ? {}
147
+ : { kind: channel.definition.meta.kind }),
148
+ })),
149
+ },
150
+ info: projectInfo(project),
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Parse an untrusted manifest (round-trip check for the CLI's own output;
156
+ * the CP re-validates server-side with its own copy of the contract).
157
+ */
158
+ export function parseAgentDeployManifest(
159
+ raw: unknown
160
+ ): AgentDeployManifestV1 | undefined {
161
+ const parsed = agentDeployManifestV1Schema.safeParse(raw);
162
+ // `info` is opaque to the wire schema (validated as a plain object) but
163
+ // typed as AgentProjectInfo for producers/consumers inside the SDK.
164
+ return parsed.success
165
+ ? (parsed.data as unknown as AgentDeployManifestV1)
166
+ : undefined;
167
+ }