@cursor/july 0.1.62 → 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 (180) hide show
  1. package/dist/bin/agent-serve.js +8 -0
  2. package/dist/channels/slack/channel-watch.d.ts +2 -1
  3. package/dist/channels/slack/channel-watch.d.ts.map +1 -1
  4. package/dist/channels/slack/channel-watch.js +7 -3
  5. package/dist/channels/slack/dispatch.d.ts +9 -0
  6. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  7. package/dist/channels/slack/dispatch.js +41 -21
  8. package/dist/channels/slack/manifest.d.ts.map +1 -1
  9. package/dist/channels/slack/manifest.js +1 -0
  10. package/dist/channels/slack/nudge-store.d.ts +16 -0
  11. package/dist/channels/slack/nudge-store.d.ts.map +1 -1
  12. package/dist/channels/slack/nudge-store.js +53 -0
  13. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  14. package/dist/channels/slack/slack-channel.js +4 -1
  15. package/dist/channels/slack/types.d.ts +22 -5
  16. package/dist/channels/slack/types.d.ts.map +1 -1
  17. package/dist/docs/404.html +2 -2
  18. package/dist/docs/ab.html +3 -3
  19. package/dist/docs/assets/{app.B-biogkN.js → app.C5XUTCw9.js} +1 -1
  20. package/dist/docs/assets/chunks/@localSearchIndexroot.DvUR3lv9.js +1 -0
  21. package/dist/docs/assets/chunks/{VPLocalSearchBox.DZqKtFZS.js → VPLocalSearchBox.CGDTAYHY.js} +1 -1
  22. package/dist/docs/assets/chunks/{theme.BwC5zP2r.js → theme.lPixh250.js} +2 -2
  23. package/dist/docs/assets/{reference_connections.md.CSQiNzre.js → reference_connections.md.Bwswbm6H.js} +13 -3
  24. package/dist/docs/assets/{reference_connections.md.CSQiNzre.lean.js → reference_connections.md.Bwswbm6H.lean.js} +1 -1
  25. package/dist/docs/assets/{reference_tools.md.kJfJAP2m.js → reference_tools.md.Wmvb7Lob.js} +2 -2
  26. package/dist/docs/assets/{reference_tools.md.kJfJAP2m.lean.js → reference_tools.md.Wmvb7Lob.lean.js} +1 -1
  27. package/dist/docs/building-with-agents.html +3 -3
  28. package/dist/docs/concepts.html +3 -3
  29. package/dist/docs/deployment.html +3 -3
  30. package/dist/docs/evals.html +3 -3
  31. package/dist/docs/example-agents/approval-buddy.html +3 -3
  32. package/dist/docs/example-agents/benny.html +3 -3
  33. package/dist/docs/example-agents/bugbot.html +3 -3
  34. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  35. package/dist/docs/example-agents/codeowners-review.html +3 -3
  36. package/dist/docs/example-agents/concierge.html +3 -3
  37. package/dist/docs/example-agents/fsd.html +3 -3
  38. package/dist/docs/example-agents/index.html +3 -3
  39. package/dist/docs/example-agents/knowledge-base.html +3 -3
  40. package/dist/docs/example-agents/oncall.html +3 -3
  41. package/dist/docs/example-agents/security-reviewer.html +3 -3
  42. package/dist/docs/example-agents/slack-agent.html +3 -3
  43. package/dist/docs/example-agents/weather-agent.html +3 -3
  44. package/dist/docs/guides/agent-to-agent.html +3 -3
  45. package/dist/docs/guides/cloud-runtime.html +3 -3
  46. package/dist/docs/guides/convert-automation.html +3 -3
  47. package/dist/docs/guides/github.html +3 -3
  48. package/dist/docs/guides/human-in-the-loop.html +3 -3
  49. package/dist/docs/guides/mcp-oauth.html +3 -3
  50. package/dist/docs/guides/opentelemetry.html +3 -3
  51. package/dist/docs/guides/slack.html +3 -3
  52. package/dist/docs/guides/webhooks.html +3 -3
  53. package/dist/docs/hashmap.json +1 -1
  54. package/dist/docs/hillclimbing.html +3 -3
  55. package/dist/docs/index.html +3 -3
  56. package/dist/docs/quickstart.html +3 -3
  57. package/dist/docs/reference/agent-config.html +3 -3
  58. package/dist/docs/reference/artifacts.html +3 -3
  59. package/dist/docs/reference/channels.html +3 -3
  60. package/dist/docs/reference/cli.html +3 -3
  61. package/dist/docs/reference/connections.html +15 -5
  62. package/dist/docs/reference/hooks.html +3 -3
  63. package/dist/docs/reference/http-api.html +3 -3
  64. package/dist/docs/reference/instructions.html +3 -3
  65. package/dist/docs/reference/playground.html +3 -3
  66. package/dist/docs/reference/project-layout.html +3 -3
  67. package/dist/docs/reference/prompt.html +3 -3
  68. package/dist/docs/reference/schedules.html +3 -3
  69. package/dist/docs/reference/sessions.html +3 -3
  70. package/dist/docs/reference/skills.html +3 -3
  71. package/dist/docs/reference/subagents.html +3 -3
  72. package/dist/docs/reference/tools.html +5 -5
  73. package/dist/docs/scaffolding-agents.html +3 -3
  74. package/dist/docs/storage.html +3 -3
  75. package/dist/docs/templates/agentic-owners.html +3 -3
  76. package/dist/docs/templates/demo.html +3 -3
  77. package/dist/docs/templates/pr-autofixer.html +3 -3
  78. package/dist/docs/templates/security-reviewer.html +3 -3
  79. package/dist/docs/templates/triage.html +3 -3
  80. package/dist/docs/troubleshooting.html +3 -3
  81. package/dist/files-backends/agent-store-presigned-url.js +1 -1
  82. package/dist/index.d.ts +2 -2
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +1 -1
  85. package/dist/internal/advertise-tools.d.ts +74 -0
  86. package/dist/internal/advertise-tools.d.ts.map +1 -0
  87. package/dist/internal/advertise-tools.js +152 -0
  88. package/dist/internal/cli-deploy.d.ts.map +1 -1
  89. package/dist/internal/cli-deploy.js +11 -3
  90. package/dist/internal/cli-manifest.d.ts +19 -0
  91. package/dist/internal/cli-manifest.d.ts.map +1 -0
  92. package/dist/internal/cli-manifest.js +49 -0
  93. package/dist/internal/cli-slack.d.ts.map +1 -1
  94. package/dist/internal/cli-slack.js +9 -1
  95. package/dist/internal/cursor-agent-template.d.ts +9 -0
  96. package/dist/internal/cursor-agent-template.d.ts.map +1 -0
  97. package/dist/internal/cursor-agent-template.js +37 -0
  98. package/dist/internal/deploy-client.d.ts +8 -0
  99. package/dist/internal/deploy-client.d.ts.map +1 -1
  100. package/dist/internal/deploy-client.js +21 -3
  101. package/dist/internal/deploy-manifest.d.ts +85 -0
  102. package/dist/internal/deploy-manifest.d.ts.map +1 -0
  103. package/dist/internal/deploy-manifest.js +106 -0
  104. package/dist/internal/deployment-scm-event-stream.d.ts +113 -0
  105. package/dist/internal/deployment-scm-event-stream.d.ts.map +1 -0
  106. package/dist/internal/deployment-scm-event-stream.js +460 -0
  107. package/dist/internal/discovery.d.ts.map +1 -1
  108. package/dist/internal/discovery.js +69 -0
  109. package/dist/internal/init-project.d.ts.map +1 -1
  110. package/dist/internal/init-project.js +5 -17
  111. package/dist/internal/mcp-host.d.ts +18 -0
  112. package/dist/internal/mcp-host.d.ts.map +1 -1
  113. package/dist/internal/mcp-host.js +41 -5
  114. package/dist/internal/sdk-runner.d.ts +1 -1
  115. package/dist/internal/sdk-runner.d.ts.map +1 -1
  116. package/dist/internal/sdk-runner.js +36 -13
  117. package/dist/internal/server.d.ts.map +1 -1
  118. package/dist/internal/server.js +18 -0
  119. package/dist/internal/session-engine.d.ts +66 -0
  120. package/dist/internal/session-engine.d.ts.map +1 -1
  121. package/dist/internal/session-engine.js +430 -110
  122. package/dist/internal/shallow-clone.d.ts +115 -0
  123. package/dist/internal/shallow-clone.d.ts.map +1 -0
  124. package/dist/internal/shallow-clone.js +292 -0
  125. package/dist/internal/slack-provision-client.d.ts +2 -0
  126. package/dist/internal/slack-provision-client.d.ts.map +1 -1
  127. package/dist/internal/slack-provision-client.js +1 -0
  128. package/dist/internal/turn-lease-store.d.ts +44 -0
  129. package/dist/internal/turn-lease-store.d.ts.map +1 -0
  130. package/dist/internal/turn-lease-store.js +139 -0
  131. package/dist/internal/workspace.d.ts +12 -0
  132. package/dist/internal/workspace.d.ts.map +1 -1
  133. package/dist/internal/workspace.js +28 -0
  134. package/dist/playground/assets/index-CoL7xCJl.css +1 -0
  135. package/dist/playground/assets/index-yxs1heea.js +85 -0
  136. package/dist/playground/index.html +2 -2
  137. package/dist/storage-protocol.d.ts +122 -0
  138. package/dist/storage-protocol.d.ts.map +1 -0
  139. package/dist/storage-protocol.js +173 -0
  140. package/dist/storage.d.ts +2 -72
  141. package/dist/storage.d.ts.map +1 -1
  142. package/dist/storage.js +2 -82
  143. package/dist/types.d.ts +113 -3
  144. package/dist/types.d.ts.map +1 -1
  145. package/docs/reference/connections.md +80 -0
  146. package/docs/reference/tools.md +14 -0
  147. package/package.json +4 -2
  148. package/src/bin/agent-serve.ts +9 -0
  149. package/src/channels/slack/channel-watch.ts +6 -3
  150. package/src/channels/slack/dispatch.ts +40 -3
  151. package/src/channels/slack/manifest.ts +1 -0
  152. package/src/channels/slack/nudge-store.ts +56 -0
  153. package/src/channels/slack/slack-channel.ts +3 -0
  154. package/src/channels/slack/types.ts +22 -5
  155. package/src/index.ts +5 -0
  156. package/src/internal/advertise-tools.ts +229 -0
  157. package/src/internal/cli-deploy.ts +22 -1
  158. package/src/internal/cli-manifest.ts +63 -0
  159. package/src/internal/cli-slack.ts +10 -1
  160. package/src/internal/cursor-agent-template.ts +46 -0
  161. package/src/internal/deploy-client.ts +35 -6
  162. package/src/internal/deploy-manifest.ts +167 -0
  163. package/src/internal/deployment-scm-event-stream.ts +577 -0
  164. package/src/internal/discovery.ts +85 -0
  165. package/src/internal/init-project.ts +11 -20
  166. package/src/internal/mcp-host.ts +66 -9
  167. package/src/internal/sdk-runner.ts +50 -11
  168. package/src/internal/server.ts +19 -0
  169. package/src/internal/session-engine.ts +385 -19
  170. package/src/internal/shallow-clone.ts +407 -0
  171. package/src/internal/slack-provision-client.ts +3 -0
  172. package/src/internal/turn-lease-store.ts +147 -0
  173. package/src/internal/workspace.ts +27 -0
  174. package/src/storage-protocol.ts +247 -0
  175. package/src/storage.ts +16 -110
  176. package/src/types.ts +117 -3
  177. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +4 -1
  178. package/dist/docs/assets/chunks/@localSearchIndexroot.5ozYNf74.js +0 -1
  179. package/dist/playground/assets/index-B4rcOgeP.css +0 -1
  180. package/dist/playground/assets/index-DXcY42Jv.js +0 -85
@@ -3,10 +3,16 @@
3
3
  */
4
4
 
5
5
  import { spawn } from "node:child_process";
6
- import { existsSync, readFileSync } from "node:fs";
6
+ import { existsSync } from "node:fs";
7
7
  import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
8
8
  import { basename, dirname, join, relative, resolve } from "node:path";
9
9
  import type { ModelSetting } from "../types.js";
10
+ import {
11
+ canonicalTemplateName,
12
+ isTemplateName,
13
+ readCursorAgentTemplate,
14
+ type TemplateName,
15
+ } from "./cursor-agent-template.js";
10
16
  import {
11
17
  CLI_COMMAND_NAME,
12
18
  PACKAGE_NAME,
@@ -64,7 +70,7 @@ type TemplateEntry = {
64
70
  overlay?: string;
65
71
  };
66
72
 
67
- const TEMPLATES: Readonly<Record<string, TemplateEntry>> = {
73
+ const TEMPLATES: Readonly<Record<TemplateName, TemplateEntry>> = {
68
74
  "agentic-owners": { dir: "agentic-owners" },
69
75
  demo: { dir: "demo" },
70
76
  "pr-autofixer": { dir: "pr-autofixer" },
@@ -73,10 +79,6 @@ const TEMPLATES: Readonly<Record<string, TemplateEntry>> = {
73
79
  "triage-jira": { dir: "triage", overlay: "jira" },
74
80
  };
75
81
 
76
- function canonicalTemplateName(name: string): string {
77
- return name === "triage" ? "triage-linear" : name;
78
- }
79
-
80
82
  export function availableTemplates(): string[] {
81
83
  return Object.keys(TEMPLATES).sort();
82
84
  }
@@ -93,13 +95,13 @@ export function resolveTemplate(name: string): TemplateResolution {
93
95
  };
94
96
  }
95
97
  const resolved = canonicalTemplateName(name);
96
- const entry = TEMPLATES[resolved];
97
- if (entry === undefined) {
98
+ if (!isTemplateName(resolved)) {
98
99
  return {
99
100
  ok: false,
100
101
  message: `unknown template "${name}" (available: ${availableTemplates().join(", ")})`,
101
102
  };
102
103
  }
104
+ const entry = TEMPLATES[resolved];
103
105
  const dir = join(packageRootDir(), "templates", entry.dir);
104
106
  return {
105
107
  ok: true,
@@ -128,18 +130,7 @@ export function checkInitTarget(
128
130
  if (!existsSync(pkgPath)) {
129
131
  return { ok: true };
130
132
  }
131
- let marker: string | undefined;
132
- try {
133
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as {
134
- cursorAgentTemplate?: unknown;
135
- };
136
- marker =
137
- typeof pkg.cursorAgentTemplate === "string"
138
- ? pkg.cursorAgentTemplate
139
- : undefined;
140
- } catch {
141
- marker = undefined;
142
- }
133
+ const marker = readCursorAgentTemplate(rootDir);
143
134
  if (marker === template) {
144
135
  return { ok: true };
145
136
  }
@@ -29,6 +29,18 @@ interface OpenedConnection {
29
29
  close: () => Promise<void>;
30
30
  }
31
31
 
32
+ /**
33
+ * A non-caching view of one connection: every operation opens a fresh
34
+ * client and closes it afterwards. See {@link McpHost.oneOff}.
35
+ */
36
+ export interface HostMcpOneOff {
37
+ listTools(): Promise<HostMcpToolInfo[]>;
38
+ callTool(
39
+ toolName: string,
40
+ args?: Record<string, unknown>
41
+ ): Promise<HostMcpCallResult>;
42
+ }
43
+
32
44
  export class McpHost implements HostMcpRegistry {
33
45
  private readonly byName = new Map<string, DiscoveredConnection>();
34
46
  private readonly clients = new Map<string, Promise<OpenedConnection>>();
@@ -54,18 +66,47 @@ export class McpHost implements HostMcpRegistry {
54
66
  async listTools(connectionName: string): Promise<HostMcpToolInfo[]> {
55
67
  return this.withClient(connectionName, async (client) => {
56
68
  const listed = await client.listTools();
57
- return listed.tools.map((tool) => ({
58
- name: tool.name,
59
- ...(tool.description === undefined
60
- ? {}
61
- : { description: tool.description }),
62
- ...(tool.inputSchema === undefined
63
- ? {}
64
- : { inputSchema: tool.inputSchema as JsonObject }),
65
- }));
69
+ return listed.tools.map(toToolInfo);
66
70
  });
67
71
  }
68
72
 
73
+ /**
74
+ * A one-off view of a connection that is not (or not exactly) in the
75
+ * registry — e.g. a registered connection with per-session auth headers
76
+ * substituted in. Every operation opens a fresh client and closes it
77
+ * afterwards: the registry's cached per-name client would leak one
78
+ * session's identity into another, and stateless streamable-HTTP
79
+ * servers answer a fresh initialize cheaply. Results ride the same
80
+ * mapping as registry calls.
81
+ */
82
+ oneOff(connection: DiscoveredConnection): HostMcpOneOff {
83
+ const withFreshClient = async <T>(
84
+ run: (client: Client) => Promise<T>
85
+ ): Promise<T> => {
86
+ const opened = await openConnection(connection);
87
+ try {
88
+ return await run(opened.client);
89
+ } finally {
90
+ await opened.close();
91
+ }
92
+ };
93
+ return {
94
+ listTools: () =>
95
+ withFreshClient(async (client) => {
96
+ const listed = await client.listTools();
97
+ return listed.tools.map(toToolInfo);
98
+ }),
99
+ callTool: (toolName, args = {}) =>
100
+ withFreshClient(async (client) => {
101
+ const result = await client.callTool({
102
+ name: toolName,
103
+ arguments: args,
104
+ });
105
+ return normalizeCallResult(result);
106
+ }),
107
+ };
108
+ }
109
+
69
110
  async callTool(
70
111
  connectionName: string,
71
112
  toolName: string,
@@ -172,6 +213,22 @@ export function isStaleMcpSessionError(error: unknown): boolean {
172
213
  return code === 404;
173
214
  }
174
215
 
216
+ function toToolInfo(tool: {
217
+ name: string;
218
+ description?: string;
219
+ inputSchema?: unknown;
220
+ }): HostMcpToolInfo {
221
+ return {
222
+ name: tool.name,
223
+ ...(tool.description === undefined
224
+ ? {}
225
+ : { description: tool.description }),
226
+ ...(tool.inputSchema === undefined
227
+ ? {}
228
+ : { inputSchema: tool.inputSchema as JsonObject }),
229
+ };
230
+ }
231
+
175
232
  async function openConnection(
176
233
  connection: DiscoveredConnection
177
234
  ): Promise<OpenedConnection> {
@@ -55,6 +55,7 @@ export type RunnerPrewarmRequest = Pick<
55
55
  | "sandbox"
56
56
  | "tools"
57
57
  | "mcpServers"
58
+ | "perSendMcpServers"
58
59
  | "subagents"
59
60
  | "model"
60
61
  >;
@@ -159,6 +160,28 @@ export function isAuthErrorMessage(message: string | undefined): boolean {
159
160
  return /authentication error|try logging out and back in/i.test(message);
160
161
  }
161
162
 
163
+ /** Cloud `wait()` copy for `stream_expired` / a missing start-state blob. */
164
+ function isStreamExpiredErrorMessage(message: string | undefined): boolean {
165
+ if (message === undefined) {
166
+ return false;
167
+ }
168
+ return /run stream is no longer available|\[stream_expired\]/i.test(message);
169
+ }
170
+
171
+ function retryReason(
172
+ message: string | undefined,
173
+ isFirstTurn: boolean
174
+ ): string | undefined {
175
+ if (isStreamExpiredErrorMessage(message)) {
176
+ return "cloud run stream expired";
177
+ }
178
+ // First-turn auth is the credential; a fresh handle cannot fix it.
179
+ if (!isFirstTurn && isAuthErrorMessage(message)) {
180
+ return "authentication failure";
181
+ }
182
+ return undefined;
183
+ }
184
+
162
185
  export class CursorSdkRunner implements AgentRunner {
163
186
  private readonly agents = new Map<string, CachedAgentHandle>();
164
187
  private readonly stores = new Map<string, Promise<LocalAgentStore>>();
@@ -187,26 +210,23 @@ export class CursorSdkRunner implements AgentRunner {
187
210
  const outcome = await this.runTurnAttempt(tracked);
188
211
  if (
189
212
  outcome.status !== "error" ||
190
- !isAuthErrorMessage(outcome.errorMessage) ||
191
213
  sawUpdate ||
192
- request.isFirstTurn ||
193
214
  request.signal?.aborted === true
194
215
  ) {
195
216
  return outcome;
196
217
  }
197
- // The turn died on an authentication failure before producing any output
198
- // the signature of a handle whose exchanged access token expired (the
199
- // failed attempt already evicted it). Retrying once on a fresh handle is
200
- // safe here because nothing streamed and no tool ran. First turns are
201
- // excluded: their token was exchanged moments ago, so the credential
202
- // itself is the problem and a retry cannot succeed.
218
+ const reason = retryReason(outcome.errorMessage, request.isFirstTurn);
219
+ if (reason === undefined) {
220
+ return outcome;
221
+ }
222
+ // Log the original error even when the retry succeeds so the expiry
223
+ // still shows up in Datadog.
203
224
  this.log(
204
- `[agent-sdk] session ${request.sessionId}: retrying the turn on a fresh SDK agent handle after an authentication failure`
225
+ `[agent-sdk] session ${request.sessionId}: ${reason}: ${outcome.errorMessage}; retrying the turn`
205
226
  );
206
227
  try {
207
228
  return await this.runTurnAttempt(tracked);
208
229
  } catch {
209
- // The retry was best effort; report the original failure.
210
230
  return outcome;
211
231
  }
212
232
  }
@@ -235,6 +255,12 @@ export class CursorSdkRunner implements AgentRunner {
235
255
  // Stream mapping must never reject into the SDK transport.
236
256
  }
237
257
  },
258
+ // The SDK builds this send's MCP session from exactly this set, so
259
+ // a turn always runs on the headers the engine evaluated for it —
260
+ // never on the ones a previous turn opened the cached handle with.
261
+ ...(request.runtime === "local" && request.perSendMcpServers === true
262
+ ? { mcpServers: request.mcpServers as AgentOptions["mcpServers"] }
263
+ : {}),
238
264
  ...(request.runtime === "local"
239
265
  ? {
240
266
  local: {
@@ -270,6 +296,14 @@ export class CursorSdkRunner implements AgentRunner {
270
296
  if (request.onSdkAgentId !== undefined) {
271
297
  await request.onSdkAgentId(agent.agentId);
272
298
  }
299
+ // Readable on the handle as soon as send resolves, long before wait().
300
+ if (
301
+ request.onRequestId !== undefined &&
302
+ run.requestId !== undefined &&
303
+ run.requestId !== ""
304
+ ) {
305
+ await request.onRequestId(run.requestId);
306
+ }
273
307
  try {
274
308
  const result = await run.wait();
275
309
  let outcome = toTurnOutcome(result);
@@ -401,7 +435,12 @@ export class CursorSdkRunner implements AgentRunner {
401
435
  return {
402
436
  model: toModelSelection(request.model ?? DEFAULT_MODEL),
403
437
  apiKey: this.requireApiKey(),
404
- ...(Object.keys(request.mcpServers).length > 0
438
+ // Per-send servers ride each agent.send instead (fresh per-session
439
+ // auth headers every turn). Keeping them out of the create/resume
440
+ // options also keeps the local executor cache key — which hashes
441
+ // these options — stable while credentials rotate.
442
+ ...(Object.keys(request.mcpServers).length > 0 &&
443
+ request.perSendMcpServers !== true
405
444
  ? { mcpServers: request.mcpServers as AgentOptions["mcpServers"] }
406
445
  : {}),
407
446
  ...(subagents === undefined ? {} : { agents: subagents }),
@@ -70,12 +70,14 @@ import {
70
70
  scmEventToGitHubPayload,
71
71
  scmMetadataToOriginPayload,
72
72
  } from "./cursor-event-relay.js";
73
+ import { AGENT_SERVE_GENERATION_ENV_VAR } from "./cursor-relay-core.js";
73
74
  import {
74
75
  type CursorSlackAgentRegistration,
75
76
  CursorSlackRelay,
76
77
  createCursorSlackApiCaller,
77
78
  formatCursorSlackEventLog,
78
79
  } from "./cursor-slack-relay.js";
80
+ import { buildAgentDeployManifest } from "./deploy-manifest.js";
79
81
  import {
80
82
  formatDiagnostics,
81
83
  hasErrorDiagnostics,
@@ -1111,6 +1113,23 @@ function mountAgentRoutes(
1111
1113
  handler: () => jsonResponse(projectInfo(project)),
1112
1114
  });
1113
1115
 
1116
+ // Hosted-only: the control plane's provision flow reads the deploy manifest
1117
+ // from the running engine (same derivation as `july manifest`) right after
1118
+ // the health probe, so every deploy surface — CLI, UI, redeploy — gets its
1119
+ // declarations from the installed code without a client-side extraction
1120
+ // step. `auth: "none"` mirrors `/v1/health`: hosted engines are reachable
1121
+ // only through the anyrun port proxy (network token) or the alias route
1122
+ // (alias token), and the route is not registered outside hosted mode, so
1123
+ // self-hosted `july serve` never exposes the manifest unauthenticated.
1124
+ if (process.env[AGENT_SERVE_GENERATION_ENV_VAR] !== undefined) {
1125
+ router.add({
1126
+ method: "GET",
1127
+ pattern: `${base}/v1/manifest`,
1128
+ auth: "none",
1129
+ handler: () => jsonResponse(buildAgentDeployManifest(project)),
1130
+ });
1131
+ }
1132
+
1114
1133
  // Playground Logs tab: recent ServeOptions.logger lines (channels, sessions,
1115
1134
  // webhook capture). `after` is an exclusive index cursor for polling.
1116
1135
  router.add({