@cursor/july 0.1.2 → 0.1.3

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 (174) 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 +389 -152
  6. package/dist/docs/404.html +2 -2
  7. package/dist/docs/ab.html +3 -3
  8. package/dist/docs/assets/{app.Oje4vhlk.js → app.BR0RbIdx.js} +1 -1
  9. package/dist/docs/assets/chunks/@localSearchIndexroot.DtIOQzGj.js +1 -0
  10. package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.qjsVTneI.js} +1 -1
  11. package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.2Kg8jIp_.js} +2 -2
  12. package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
  13. package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
  14. package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
  15. package/dist/docs/building-with-agents.html +3 -3
  16. package/dist/docs/concepts.html +3 -3
  17. package/dist/docs/deployment.html +3 -3
  18. package/dist/docs/evals.html +3 -3
  19. package/dist/docs/guides/agent-to-agent.html +3 -3
  20. package/dist/docs/guides/cloud-runtime.html +3 -3
  21. package/dist/docs/guides/github.html +3 -3
  22. package/dist/docs/guides/human-in-the-loop.html +3 -3
  23. package/dist/docs/guides/slack.html +3 -3
  24. package/dist/docs/guides/webhooks.html +3 -3
  25. package/dist/docs/hashmap.json +1 -1
  26. package/dist/docs/hillclimbing.html +3 -3
  27. package/dist/docs/index.html +3 -3
  28. package/dist/docs/quickstart.html +22 -10
  29. package/dist/docs/reference/agent-config.html +3 -3
  30. package/dist/docs/reference/channels.html +3 -3
  31. package/dist/docs/reference/cli.html +14 -9
  32. package/dist/docs/reference/connections.html +3 -3
  33. package/dist/docs/reference/hooks.html +3 -3
  34. package/dist/docs/reference/http-api.html +3 -3
  35. package/dist/docs/reference/instructions.html +3 -3
  36. package/dist/docs/reference/playground.html +3 -3
  37. package/dist/docs/reference/project-layout.html +3 -3
  38. package/dist/docs/reference/schedules.html +3 -3
  39. package/dist/docs/reference/sessions.html +3 -3
  40. package/dist/docs/reference/skills.html +3 -3
  41. package/dist/docs/reference/subagents.html +3 -3
  42. package/dist/docs/reference/tools.html +3 -3
  43. package/dist/docs/scaffolding-agents.html +3 -3
  44. package/dist/docs/storage.html +3 -3
  45. package/dist/docs/troubleshooting.html +3 -3
  46. package/dist/internal/chat-client.d.ts +29 -3
  47. package/dist/internal/chat-client.d.ts.map +1 -1
  48. package/dist/internal/chat-client.js +180 -27
  49. package/dist/internal/cli-ax.d.ts +75 -2
  50. package/dist/internal/cli-ax.d.ts.map +1 -1
  51. package/dist/internal/cli-ax.js +600 -52
  52. package/dist/internal/cli-cursor.d.ts.map +1 -1
  53. package/dist/internal/cli-cursor.js +8 -36
  54. package/dist/internal/cli-deploy.d.ts +59 -3
  55. package/dist/internal/cli-deploy.d.ts.map +1 -1
  56. package/dist/internal/cli-deploy.js +316 -38
  57. package/dist/internal/cursor/credentials.d.ts +9 -0
  58. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  59. package/dist/internal/cursor/credentials.js +12 -0
  60. package/dist/internal/deploy-client.d.ts +9 -1
  61. package/dist/internal/deploy-client.d.ts.map +1 -1
  62. package/dist/internal/deploy-client.js +23 -3
  63. package/dist/internal/deploy-source.d.ts +35 -0
  64. package/dist/internal/deploy-source.d.ts.map +1 -0
  65. package/dist/internal/deploy-source.js +118 -0
  66. package/dist/internal/discovery.d.ts +11 -0
  67. package/dist/internal/discovery.d.ts.map +1 -1
  68. package/dist/internal/discovery.js +43 -14
  69. package/dist/internal/eval-run-store.d.ts.map +1 -1
  70. package/dist/internal/eval-run-store.js +2 -1
  71. package/dist/internal/eval-runner.d.ts +2 -0
  72. package/dist/internal/eval-runner.d.ts.map +1 -1
  73. package/dist/internal/eval-runner.js +2 -0
  74. package/dist/internal/event-mapper.d.ts +54 -1
  75. package/dist/internal/event-mapper.d.ts.map +1 -1
  76. package/dist/internal/event-mapper.js +151 -41
  77. package/dist/internal/init-project.d.ts +88 -1
  78. package/dist/internal/init-project.d.ts.map +1 -1
  79. package/dist/internal/init-project.js +221 -31
  80. package/dist/internal/install-cursor-skills.d.ts +64 -0
  81. package/dist/internal/install-cursor-skills.d.ts.map +1 -0
  82. package/dist/internal/install-cursor-skills.js +274 -0
  83. package/dist/internal/logs-client.d.ts +60 -0
  84. package/dist/internal/logs-client.d.ts.map +1 -0
  85. package/dist/internal/logs-client.js +311 -0
  86. package/dist/internal/open-browser.d.ts +5 -0
  87. package/dist/internal/open-browser.d.ts.map +1 -0
  88. package/dist/internal/open-browser.js +34 -0
  89. package/dist/internal/playground/toolchain.d.ts.map +1 -1
  90. package/dist/internal/playground/toolchain.js +16 -11
  91. package/dist/internal/playground-proxy.d.ts +69 -0
  92. package/dist/internal/playground-proxy.d.ts.map +1 -0
  93. package/dist/internal/playground-proxy.js +468 -0
  94. package/dist/internal/prompt-context.d.ts +35 -0
  95. package/dist/internal/prompt-context.d.ts.map +1 -0
  96. package/dist/internal/prompt-context.js +71 -0
  97. package/dist/internal/request-headers.d.ts +11 -0
  98. package/dist/internal/request-headers.d.ts.map +1 -0
  99. package/dist/internal/request-headers.js +14 -0
  100. package/dist/internal/resolve-prod-target.d.ts +42 -0
  101. package/dist/internal/resolve-prod-target.d.ts.map +1 -0
  102. package/dist/internal/resolve-prod-target.js +111 -0
  103. package/dist/internal/run-client.d.ts +2 -2
  104. package/dist/internal/run-client.d.ts.map +1 -1
  105. package/dist/internal/run-client.js +38 -23
  106. package/dist/internal/server.d.ts.map +1 -1
  107. package/dist/internal/server.js +10 -4
  108. package/dist/internal/session-engine.d.ts +1 -1
  109. package/dist/internal/session-engine.d.ts.map +1 -1
  110. package/dist/internal/session-engine.js +21 -6
  111. package/dist/internal/sessions-client.d.ts +21 -0
  112. package/dist/internal/sessions-client.d.ts.map +1 -0
  113. package/dist/internal/sessions-client.js +168 -0
  114. package/dist/internal/stream-progress.d.ts.map +1 -1
  115. package/dist/internal/stream-progress.js +31 -3
  116. package/dist/internal/terminal-style.d.ts +22 -0
  117. package/dist/internal/terminal-style.d.ts.map +1 -0
  118. package/dist/internal/terminal-style.js +49 -0
  119. package/dist/internal/trajectory.d.ts +10 -5
  120. package/dist/internal/trajectory.d.ts.map +1 -1
  121. package/dist/internal/trajectory.js +82 -10
  122. package/dist/internal/workspace.d.ts +11 -0
  123. package/dist/internal/workspace.d.ts.map +1 -1
  124. package/dist/internal/workspace.js +38 -4
  125. package/dist/playground/assets/index-D-vo2lV_.css +1 -0
  126. package/dist/playground/assets/index-x60b9q2j.js +312 -0
  127. package/dist/playground/index.html +2 -2
  128. package/dist/types.d.ts +26 -1
  129. package/dist/types.d.ts.map +1 -1
  130. package/docs/quickstart.md +22 -7
  131. package/docs/reference/cli.md +51 -3
  132. package/package.json +3 -1
  133. package/skills/ab/SKILL.md +8 -8
  134. package/skills/create-agent/SKILL.md +28 -22
  135. package/skills/debug/SKILL.md +11 -11
  136. package/skills/evals/SKILL.md +16 -16
  137. package/skills/framework-map/SKILL.md +6 -6
  138. package/skills/github/SKILL.md +13 -13
  139. package/skills/hillclimb/SKILL.md +10 -10
  140. package/skills/setup-slack/SKILL.md +13 -13
  141. package/src/bin/agent-serve.ts +422 -188
  142. package/src/internal/chat-client.ts +252 -37
  143. package/src/internal/cli-ax.ts +722 -72
  144. package/src/internal/cli-cursor.ts +14 -42
  145. package/src/internal/cli-deploy.ts +428 -49
  146. package/src/internal/cursor/credentials.ts +14 -0
  147. package/src/internal/deploy-client.ts +45 -4
  148. package/src/internal/deploy-source.ts +133 -0
  149. package/src/internal/discovery.ts +53 -16
  150. package/src/internal/eval-run-store.ts +2 -1
  151. package/src/internal/eval-runner.ts +5 -0
  152. package/src/internal/event-mapper.ts +222 -42
  153. package/src/internal/init-project.ts +333 -51
  154. package/src/internal/install-cursor-skills.ts +327 -0
  155. package/src/internal/logs-client.ts +442 -0
  156. package/src/internal/open-browser.ts +41 -0
  157. package/src/internal/playground/toolchain.ts +15 -13
  158. package/src/internal/playground-proxy.ts +576 -0
  159. package/src/internal/prompt-context.ts +95 -0
  160. package/src/internal/request-headers.ts +23 -0
  161. package/src/internal/resolve-prod-target.ts +150 -0
  162. package/src/internal/run-client.ts +39 -36
  163. package/src/internal/server.ts +12 -3
  164. package/src/internal/session-engine.ts +22 -3
  165. package/src/internal/sessions-client.ts +182 -0
  166. package/src/internal/stream-progress.ts +36 -2
  167. package/src/internal/terminal-style.ts +74 -0
  168. package/src/internal/trajectory.ts +91 -13
  169. package/src/internal/workspace.ts +42 -4
  170. package/src/types.ts +42 -6
  171. package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
  172. package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
  173. package/dist/playground/assets/index-CpDYCj8W.js +0 -79
  174. /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
@@ -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,
@@ -400,7 +400,8 @@ export class EvalRunStore {
400
400
  }
401
401
  if (
402
402
  event.type === "message.completed" &&
403
- typeof event.data?.text === "string"
403
+ typeof event.data?.text === "string" &&
404
+ event.data.parentCallId === undefined
404
405
  ) {
405
406
  c.finalText = event.data.text;
406
407
  }
@@ -76,6 +76,8 @@ export interface RunEvalsOptions extends EvalSelectionOptions {
76
76
  baseUrl: string;
77
77
  timeoutMs?: number;
78
78
  bearerToken?: string;
79
+ /** Extra request headers (e.g. hosted engineAccess). */
80
+ headers?: Record<string, string>;
79
81
  verbose?: boolean;
80
82
  onEvent?: (event: SessionEvent) => void;
81
83
  /** Fired when a case begins executing (after concurrency slot acquired). */
@@ -240,6 +242,7 @@ export async function runDiscoveredEvals(
240
242
  baseUrl: options.baseUrl,
241
243
  defaultTimeoutMs,
242
244
  bearerToken: options.bearerToken,
245
+ headers: options.headers,
243
246
  verbose: Boolean(options.verbose),
244
247
  onEvent: (event) => {
245
248
  options.onCaseEvent?.(discovered.id, event);
@@ -268,6 +271,7 @@ async function runOneEval(
268
271
  baseUrl: string;
269
272
  defaultTimeoutMs: number;
270
273
  bearerToken?: string;
274
+ headers?: Record<string, string>;
271
275
  verbose: boolean;
272
276
  onEvent?: (event: SessionEvent) => void;
273
277
  }
@@ -330,6 +334,7 @@ async function runOneEval(
330
334
  // Keep defineEval traffic out of live defineAB enrollment/metrics.
331
335
  purpose: "eval",
332
336
  bearerToken: opts.bearerToken,
337
+ headers: opts.headers,
333
338
  workspaceDir: options.workspaceDir,
334
339
  workspaceFiles: options.workspaceFiles,
335
340
  timeoutMs,