@cursor/july 0.1.16 → 0.1.18

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 (273) hide show
  1. package/README.md +9 -4
  2. package/dist/bin/agent-serve.js +60 -2
  3. package/dist/channels/slack/api.d.ts.map +1 -1
  4. package/dist/channels/slack/api.js +13 -3
  5. package/dist/channels/slack/auth.d.ts +7 -1
  6. package/dist/channels/slack/auth.d.ts.map +1 -1
  7. package/dist/channels/slack/auth.js +14 -0
  8. package/dist/channels/slack/blocks.d.ts +61 -0
  9. package/dist/channels/slack/blocks.d.ts.map +1 -0
  10. package/dist/channels/slack/blocks.js +90 -0
  11. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  12. package/dist/channels/slack/cursor-account.js +1 -1
  13. package/dist/channels/slack/defaults.d.ts +2 -0
  14. package/dist/channels/slack/defaults.d.ts.map +1 -1
  15. package/dist/channels/slack/defaults.js +29 -3
  16. package/dist/channels/slack/dispatch.d.ts +3 -17
  17. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  18. package/dist/channels/slack/dispatch.js +4 -1
  19. package/dist/channels/slack/inbound.d.ts.map +1 -1
  20. package/dist/channels/slack/inbound.js +6 -1
  21. package/dist/channels/slack/index.d.ts +2 -0
  22. package/dist/channels/slack/index.d.ts.map +1 -1
  23. package/dist/channels/slack/index.js +2 -0
  24. package/dist/channels/slack/init.d.ts.map +1 -1
  25. package/dist/channels/slack/init.js +4 -1
  26. package/dist/channels/slack/interactive.d.ts +39 -3
  27. package/dist/channels/slack/interactive.d.ts.map +1 -1
  28. package/dist/channels/slack/interactive.js +153 -7
  29. package/dist/channels/slack/manifest.d.ts +13 -0
  30. package/dist/channels/slack/manifest.d.ts.map +1 -1
  31. package/dist/channels/slack/manifest.js +36 -14
  32. package/dist/channels/slack/message-body.d.ts +13 -0
  33. package/dist/channels/slack/message-body.d.ts.map +1 -0
  34. package/dist/channels/slack/message-body.js +82 -0
  35. package/dist/channels/slack/setup.d.ts +4 -1
  36. package/dist/channels/slack/setup.d.ts.map +1 -1
  37. package/dist/channels/slack/setup.js +16 -2
  38. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  39. package/dist/channels/slack/slack-channel.js +24 -3
  40. package/dist/channels/slack/thread-context.d.ts +0 -2
  41. package/dist/channels/slack/thread-context.d.ts.map +1 -1
  42. package/dist/channels/slack/thread-context.js +1 -65
  43. package/dist/channels/slack/types.d.ts +93 -1
  44. package/dist/channels/slack/types.d.ts.map +1 -1
  45. package/dist/connections.d.ts +4 -1
  46. package/dist/connections.d.ts.map +1 -1
  47. package/dist/connections.js +4 -1
  48. package/dist/docs/404.html +2 -2
  49. package/dist/docs/ab.html +3 -3
  50. package/dist/docs/assets/{app.DjHC-Ggf.js → app.BR5EdVSq.js} +1 -1
  51. package/dist/docs/assets/chunks/@localSearchIndexroot.BOzkeUi8.js +1 -0
  52. package/dist/docs/assets/chunks/{VPLocalSearchBox.DwviwmIq.js → VPLocalSearchBox.CNCzfUKC.js} +1 -1
  53. package/dist/docs/assets/chunks/{theme.BIpeYSon.js → theme.CwKvMSWv.js} +2 -2
  54. package/dist/docs/assets/{example-agents_security-reviewer.md.BZSNycMf.js → example-agents_security-reviewer.md.Dhj_m7_B.js} +1 -1
  55. package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.js → guides_mcp-oauth.md.Dd8EgSem.js} +3 -2
  56. package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.lean.js → guides_mcp-oauth.md.Dd8EgSem.lean.js} +1 -1
  57. package/dist/docs/assets/{guides_slack.md.FWale6Ip.js → guides_slack.md.D_xFKlnF.js} +1 -1
  58. package/dist/docs/assets/{reference_connections.md.CsSr3rDz.js → reference_connections.md.B9Q3TOve.js} +12 -5
  59. package/dist/docs/assets/{reference_connections.md.CsSr3rDz.lean.js → reference_connections.md.B9Q3TOve.lean.js} +1 -1
  60. package/dist/docs/building-with-agents.html +3 -3
  61. package/dist/docs/concepts.html +3 -3
  62. package/dist/docs/deployment.html +3 -3
  63. package/dist/docs/evals.html +3 -3
  64. package/dist/docs/example-agents/approval-buddy.html +3 -3
  65. package/dist/docs/example-agents/benny.html +3 -3
  66. package/dist/docs/example-agents/bugbot.html +3 -3
  67. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  68. package/dist/docs/example-agents/codeowners-review.html +3 -3
  69. package/dist/docs/example-agents/concierge.html +3 -3
  70. package/dist/docs/example-agents/fsd.html +3 -3
  71. package/dist/docs/example-agents/index.html +3 -3
  72. package/dist/docs/example-agents/knowledge-base.html +3 -3
  73. package/dist/docs/example-agents/oncall.html +3 -3
  74. package/dist/docs/example-agents/security-reviewer.html +5 -5
  75. package/dist/docs/example-agents/slack-agent.html +3 -3
  76. package/dist/docs/example-agents/weather-agent.html +3 -3
  77. package/dist/docs/guides/agent-to-agent.html +3 -3
  78. package/dist/docs/guides/cloud-runtime.html +3 -3
  79. package/dist/docs/guides/github.html +3 -3
  80. package/dist/docs/guides/human-in-the-loop.html +3 -3
  81. package/dist/docs/guides/mcp-oauth.html +6 -5
  82. package/dist/docs/guides/slack.html +5 -5
  83. package/dist/docs/guides/webhooks.html +3 -3
  84. package/dist/docs/hashmap.json +1 -1
  85. package/dist/docs/hillclimbing.html +3 -3
  86. package/dist/docs/index.html +3 -3
  87. package/dist/docs/quickstart.html +3 -3
  88. package/dist/docs/reference/agent-config.html +3 -3
  89. package/dist/docs/reference/channels.html +3 -3
  90. package/dist/docs/reference/cli.html +3 -3
  91. package/dist/docs/reference/connections.html +14 -7
  92. package/dist/docs/reference/hooks.html +3 -3
  93. package/dist/docs/reference/http-api.html +3 -3
  94. package/dist/docs/reference/instructions.html +3 -3
  95. package/dist/docs/reference/playground.html +3 -3
  96. package/dist/docs/reference/project-layout.html +3 -3
  97. package/dist/docs/reference/prompt.html +3 -3
  98. package/dist/docs/reference/schedules.html +3 -3
  99. package/dist/docs/reference/sessions.html +3 -3
  100. package/dist/docs/reference/skills.html +3 -3
  101. package/dist/docs/reference/subagents.html +3 -3
  102. package/dist/docs/reference/tools.html +3 -3
  103. package/dist/docs/scaffolding-agents.html +3 -3
  104. package/dist/docs/storage.html +3 -3
  105. package/dist/docs/troubleshooting.html +3 -3
  106. package/dist/evals/assertions.d.ts +190 -0
  107. package/dist/evals/assertions.d.ts.map +1 -0
  108. package/dist/evals/assertions.js +461 -0
  109. package/dist/evals/expect.d.ts +95 -0
  110. package/dist/evals/expect.d.ts.map +1 -0
  111. package/dist/evals/expect.js +194 -0
  112. package/dist/evals/judge.d.ts +133 -0
  113. package/dist/evals/judge.d.ts.map +1 -0
  114. package/dist/evals/judge.js +228 -0
  115. package/dist/evals/loaders.d.ts +51 -0
  116. package/dist/evals/loaders.d.ts.map +1 -0
  117. package/dist/evals/loaders.js +124 -0
  118. package/dist/evals/matchers.d.ts +95 -0
  119. package/dist/evals/matchers.d.ts.map +1 -0
  120. package/dist/evals/matchers.js +145 -0
  121. package/dist/evals/reporters.d.ts +70 -0
  122. package/dist/evals/reporters.d.ts.map +1 -0
  123. package/dist/evals/reporters.js +221 -0
  124. package/dist/evals/results.d.ts +103 -0
  125. package/dist/evals/results.d.ts.map +1 -0
  126. package/dist/evals/results.js +28 -0
  127. package/dist/evals/run-facts.d.ts +77 -0
  128. package/dist/evals/run-facts.d.ts.map +1 -0
  129. package/dist/evals/run-facts.js +187 -0
  130. package/dist/evals.d.ts +118 -31
  131. package/dist/evals.d.ts.map +1 -1
  132. package/dist/evals.js +46 -12
  133. package/dist/index.d.ts +1 -1
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +1 -1
  136. package/dist/internal/builtin-tools/index.d.ts +10 -0
  137. package/dist/internal/builtin-tools/index.d.ts.map +1 -0
  138. package/dist/internal/builtin-tools/index.js +27 -0
  139. package/dist/internal/builtin-tools/reminders.d.ts +9 -0
  140. package/dist/internal/builtin-tools/reminders.d.ts.map +1 -0
  141. package/dist/internal/builtin-tools/reminders.js +138 -0
  142. package/dist/internal/cli-ax.d.ts +27 -0
  143. package/dist/internal/cli-ax.d.ts.map +1 -1
  144. package/dist/internal/cli-ax.js +90 -12
  145. package/dist/internal/cli-mcp.d.ts +8 -0
  146. package/dist/internal/cli-mcp.d.ts.map +1 -1
  147. package/dist/internal/cli-mcp.js +98 -0
  148. package/dist/internal/cli-slack.d.ts +29 -1
  149. package/dist/internal/cli-slack.d.ts.map +1 -1
  150. package/dist/internal/cli-slack.js +799 -13
  151. package/dist/internal/cursor-event-relay.d.ts +30 -0
  152. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  153. package/dist/internal/cursor-event-relay.js +37 -0
  154. package/dist/internal/deploy-client.d.ts +7 -0
  155. package/dist/internal/deploy-client.d.ts.map +1 -1
  156. package/dist/internal/deploy-client.js +9 -0
  157. package/dist/internal/discovery.d.ts.map +1 -1
  158. package/dist/internal/discovery.js +70 -9
  159. package/dist/internal/distribution.d.ts.map +1 -1
  160. package/dist/internal/distribution.js +2 -0
  161. package/dist/internal/eval-judge-model.d.ts +59 -0
  162. package/dist/internal/eval-judge-model.d.ts.map +1 -0
  163. package/dist/internal/eval-judge-model.js +131 -0
  164. package/dist/internal/eval-run-store.d.ts.map +1 -1
  165. package/dist/internal/eval-run-store.js +12 -20
  166. package/dist/internal/eval-runner.d.ts +31 -25
  167. package/dist/internal/eval-runner.d.ts.map +1 -1
  168. package/dist/internal/eval-runner.js +329 -126
  169. package/dist/internal/evals-client.d.ts.map +1 -1
  170. package/dist/internal/evals-client.js +4 -1
  171. package/dist/internal/local-env.d.ts +8 -0
  172. package/dist/internal/local-env.d.ts.map +1 -0
  173. package/dist/internal/local-env.js +47 -0
  174. package/dist/internal/playground/static.d.ts +3 -0
  175. package/dist/internal/playground/static.d.ts.map +1 -1
  176. package/dist/internal/playground/static.js +3 -0
  177. package/dist/internal/reminder-runner.d.ts +7 -0
  178. package/dist/internal/reminder-runner.d.ts.map +1 -1
  179. package/dist/internal/reminder-runner.js +21 -0
  180. package/dist/internal/run-client.d.ts +15 -0
  181. package/dist/internal/run-client.d.ts.map +1 -1
  182. package/dist/internal/run-client.js +3 -2
  183. package/dist/internal/sdk-runner.d.ts +51 -1
  184. package/dist/internal/sdk-runner.d.ts.map +1 -1
  185. package/dist/internal/sdk-runner.js +176 -36
  186. package/dist/internal/server.d.ts.map +1 -1
  187. package/dist/internal/server.js +11 -1
  188. package/dist/internal/session-engine.d.ts +18 -5
  189. package/dist/internal/session-engine.d.ts.map +1 -1
  190. package/dist/internal/session-engine.js +81 -19
  191. package/dist/internal/slack-provision-client.d.ts +133 -0
  192. package/dist/internal/slack-provision-client.d.ts.map +1 -0
  193. package/dist/internal/slack-provision-client.js +286 -0
  194. package/dist/playground/assets/cursor-icons-16-f_W_ogc-.woff2 +0 -0
  195. package/dist/playground/assets/index-DRjS5klQ.js +85 -0
  196. package/dist/playground/assets/index-TCDs-UhZ.css +1 -0
  197. package/dist/playground/index.html +2 -2
  198. package/dist/types.d.ts +98 -4
  199. package/dist/types.d.ts.map +1 -1
  200. package/dist/types.js +26 -0
  201. package/docs/example-agents/security-reviewer.md +1 -1
  202. package/docs/guides/mcp-oauth.md +10 -5
  203. package/docs/guides/slack.md +3 -2
  204. package/docs/reference/connections.md +28 -18
  205. package/package.json +20 -2
  206. package/skills/create-agent/SKILL.md +15 -7
  207. package/skills/debug/SKILL.md +3 -1
  208. package/skills/mcp-auth/SKILL.md +1 -1
  209. package/skills/setup-slack/SKILL.md +177 -11
  210. package/src/bin/agent-serve.ts +63 -2
  211. package/src/channels/slack/api.ts +16 -3
  212. package/src/channels/slack/auth.ts +16 -1
  213. package/src/channels/slack/blocks.ts +139 -0
  214. package/src/channels/slack/cursor-account.ts +1 -0
  215. package/src/channels/slack/defaults.ts +32 -4
  216. package/src/channels/slack/dispatch.ts +6 -19
  217. package/src/channels/slack/inbound.ts +6 -1
  218. package/src/channels/slack/index.ts +2 -0
  219. package/src/channels/slack/init.ts +3 -0
  220. package/src/channels/slack/interactive.ts +184 -19
  221. package/src/channels/slack/manifest.ts +37 -6
  222. package/src/channels/slack/message-body.ts +90 -0
  223. package/src/channels/slack/setup.ts +16 -2
  224. package/src/channels/slack/slack-channel.ts +37 -6
  225. package/src/channels/slack/thread-context.ts +1 -69
  226. package/src/channels/slack/types.ts +105 -1
  227. package/src/connections.ts +4 -1
  228. package/src/evals/assertions.ts +704 -0
  229. package/src/evals/expect.ts +272 -0
  230. package/src/evals/judge.ts +346 -0
  231. package/src/evals/loaders.ts +113 -0
  232. package/src/evals/matchers.ts +246 -0
  233. package/src/evals/reporters.ts +274 -0
  234. package/src/evals/results.ts +132 -0
  235. package/src/evals/run-facts.ts +274 -0
  236. package/src/evals.ts +205 -42
  237. package/src/index.ts +2 -0
  238. package/src/internal/builtin-tools/index.ts +40 -0
  239. package/src/internal/builtin-tools/reminders.ts +158 -0
  240. package/src/internal/cli-ax.ts +134 -15
  241. package/src/internal/cli-mcp.ts +118 -5
  242. package/src/internal/cli-slack.ts +1017 -15
  243. package/src/internal/cursor-event-relay.ts +67 -0
  244. package/src/internal/deploy-client.ts +10 -0
  245. package/src/internal/discovery.ts +83 -5
  246. package/src/internal/distribution.ts +2 -0
  247. package/src/internal/eval-judge-model.ts +148 -0
  248. package/src/internal/eval-run-store.ts +10 -14
  249. package/src/internal/eval-runner.ts +460 -214
  250. package/src/internal/evals-client.ts +5 -0
  251. package/src/internal/local-env.ts +53 -0
  252. package/src/internal/playground/static.ts +3 -0
  253. package/src/internal/reminder-runner.ts +30 -0
  254. package/src/internal/run-client.ts +17 -1
  255. package/src/internal/sdk-runner.ts +234 -35
  256. package/src/internal/server.ts +12 -0
  257. package/src/internal/session-engine.ts +103 -21
  258. package/src/internal/slack-provision-client.ts +442 -0
  259. package/src/types.ts +125 -4
  260. package/dist/channels/slack/post-update-delivery.d.ts +0 -85
  261. package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
  262. package/dist/docs/assets/chunks/@localSearchIndexroot.DZtu0ZIc.js +0 -1
  263. package/dist/internal/json-dir-store.d.ts +0 -32
  264. package/dist/internal/json-dir-store.d.ts.map +0 -1
  265. package/dist/internal/persistence-coordinator.d.ts +0 -127
  266. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  267. package/dist/persistence.d.ts +0 -184
  268. package/dist/persistence.d.ts.map +0 -1
  269. package/dist/playground/assets/cursor-icons-16-CQ50JpfO.woff2 +0 -0
  270. package/dist/playground/assets/index-Bwl4Onx4.js +0 -85
  271. package/dist/playground/assets/index-ByV4nfch.css +0 -1
  272. /package/dist/docs/assets/{example-agents_security-reviewer.md.BZSNycMf.lean.js → example-agents_security-reviewer.md.Dhj_m7_B.lean.js} +0 -0
  273. /package/dist/docs/assets/{guides_slack.md.FWale6Ip.lean.js → guides_slack.md.D_xFKlnF.lean.js} +0 -0
@@ -10,7 +10,13 @@
10
10
  import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
11
11
  import { tmpdir } from "node:os";
12
12
  import { join, resolve } from "node:path";
13
- import type { EvalRunSnapshot } from "../evals.js";
13
+ import { Artifacts, JUnit } from "../evals/reporters.js";
14
+ import type {
15
+ EvalAssertionResult,
16
+ EvalReporter,
17
+ EvalRunSnapshot,
18
+ EvalVerdict,
19
+ } from "../evals.js";
14
20
  import { serve } from "../index.js";
15
21
  import type { AgentServeHandle } from "../types.js";
16
22
  import { lookupSessionContinuation, runChat } from "./chat-client.js";
@@ -96,6 +102,19 @@ export interface AxCliOptions {
96
102
  tags?: string[];
97
103
  /** Eval ids / path prefixes. */
98
104
  evalIds?: string[];
105
+ /** `eval`: a soft threshold miss (`scored`) also fails the exit code. */
106
+ strict?: boolean;
107
+ /** `eval`: write JUnit XML to this path. */
108
+ junit?: string;
109
+ /**
110
+ * `eval`: run-artifact directory. Defaults to a timestamped directory under
111
+ * `<dir>/.agent-serve/evals/`; `false` disables artifacts entirely.
112
+ */
113
+ artifacts?: string | false;
114
+ /** `eval`: skip reporters declared in `evals.config.ts` and eval files. */
115
+ skipReport?: boolean;
116
+ /** `eval`: override `evals.config.ts` `maxConcurrency`. */
117
+ maxConcurrency?: number;
99
118
  /** Scaffold target directory for `init`. */
100
119
  initTarget?: string;
101
120
  /** Tool name for `call`. */
@@ -839,6 +858,7 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
839
858
  // batch lands in playground history and defineStorage evals (when set).
840
859
  if (mode === "local") {
841
860
  const progress = stream ? createStreamProgress() : undefined;
861
+ const artifactsDir = resolveEvalArtifactsDir(projectRoot, options);
842
862
  try {
843
863
  const results = await runDiscoveredEvals({
844
864
  projectRoot,
@@ -846,6 +866,11 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
846
866
  filterIds,
847
867
  tags,
848
868
  timeoutMs: options.timeoutMs,
869
+ maxConcurrency: options.maxConcurrency,
870
+ strict: options.strict === true,
871
+ skipReport: options.skipReport === true,
872
+ reporters: buildCliEvalReporters(options, artifactsDir),
873
+ target: { baseUrl: target.agentUrl, mode: "local" },
849
874
  bearerToken: options.bearerToken,
850
875
  headers: target.headers,
851
876
  verbose: Boolean(options.verbose) || stream,
@@ -856,7 +881,7 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
856
881
  process.stderr.write("No matching evals found.\n");
857
882
  return 2;
858
883
  }
859
- return printEvalResults(results, options);
884
+ return printEvalResults(results, options, undefined, artifactsDir);
860
885
  } finally {
861
886
  progress?.close();
862
887
  }
@@ -1359,6 +1384,53 @@ function createRemoteEvalProgress(
1359
1384
  };
1360
1385
  }
1361
1386
 
1387
+ /**
1388
+ * Where a local run writes its artifacts.
1389
+ *
1390
+ * Defaults to a timestamped directory under the project's gitignored
1391
+ * `.agent-serve/evals/`, so the full story of a failure is always on disk
1392
+ * without the eval author opting in. `--no-artifacts` turns it off.
1393
+ */
1394
+ export function resolveEvalArtifactsDir(
1395
+ projectRoot: string,
1396
+ options: Pick<AxCliOptions, "artifacts">
1397
+ ): string | undefined {
1398
+ if (options.artifacts === false) {
1399
+ return undefined;
1400
+ }
1401
+ if (typeof options.artifacts === "string" && options.artifacts !== "") {
1402
+ return resolve(options.artifacts);
1403
+ }
1404
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
1405
+ return join(projectRoot, ".agent-serve", "evals", stamp);
1406
+ }
1407
+
1408
+ /** JUnit / artifacts reporters requested via CLI flags. */
1409
+ function buildCliEvalReporters(
1410
+ options: AxCliOptions,
1411
+ artifactsDir: string | undefined
1412
+ ): EvalReporter[] {
1413
+ const reporters: EvalReporter[] = [];
1414
+ if (artifactsDir !== undefined) {
1415
+ reporters.push(Artifacts({ dir: artifactsDir }));
1416
+ }
1417
+ if (options.junit !== undefined && options.junit !== "") {
1418
+ reporters.push(JUnit({ filePath: options.junit }));
1419
+ }
1420
+ return reporters;
1421
+ }
1422
+
1423
+ function evalVerdictOf(result: EvalRunResult): EvalVerdict {
1424
+ return result.verdict ?? (result.ok ? "passed" : "failed");
1425
+ }
1426
+
1427
+ const EVAL_VERDICT_LABEL: Record<EvalVerdict, string> = {
1428
+ passed: "PASS",
1429
+ failed: "FAIL",
1430
+ scored: "SCORED",
1431
+ skipped: "SKIP",
1432
+ };
1433
+
1362
1434
  function printEvalResults(
1363
1435
  results: EvalRunResult[],
1364
1436
  options: AxCliOptions,
@@ -1366,12 +1438,17 @@ function printEvalResults(
1366
1438
  runId?: string;
1367
1439
  status?: EvalRunSnapshot["status"];
1368
1440
  error?: string;
1369
- }
1441
+ },
1442
+ artifactsDir?: string
1370
1443
  ): number {
1371
- const passed = results.filter((r) => r.ok).length;
1372
- const failed = results.length - passed;
1444
+ const strict = options.strict === true;
1445
+ const counts = { passed: 0, failed: 0, scored: 0, skipped: 0 };
1446
+ for (const result of results) {
1447
+ counts[evalVerdictOf(result)]++;
1448
+ }
1373
1449
  const batchFailed = batch?.status === "failed";
1374
- const ok = failed === 0 && !batchFailed;
1450
+ const ok =
1451
+ counts.failed === 0 && !(strict && counts.scored > 0) && !batchFailed;
1375
1452
 
1376
1453
  if (batch?.error !== undefined && batch.error !== "" && !options.json) {
1377
1454
  // Always surface harness-level failures (not only --verbose).
@@ -1383,9 +1460,13 @@ function printEvalResults(
1383
1460
  JSON.stringify(
1384
1461
  {
1385
1462
  ok,
1386
- passed,
1387
- failed,
1463
+ passed: counts.passed,
1464
+ failed: counts.failed,
1465
+ scored: counts.scored,
1466
+ skipped: counts.skipped,
1467
+ strict,
1388
1468
  results,
1469
+ ...(artifactsDir === undefined ? {} : { artifactsDir }),
1389
1470
  ...(batch?.runId === undefined ? {} : { runId: batch.runId }),
1390
1471
  ...(batch?.status === undefined ? {} : { status: batch.status }),
1391
1472
  ...(batch?.error === undefined || batch.error === ""
@@ -1398,19 +1479,19 @@ function printEvalResults(
1398
1479
  );
1399
1480
  } else {
1400
1481
  for (const r of results) {
1482
+ const verdict = evalVerdictOf(r);
1401
1483
  process.stdout.write(
1402
- `${r.ok ? "PASS" : "FAIL"} ${r.id} (${(r.durationMs / 1000).toFixed(1)}s)`
1484
+ `${EVAL_VERDICT_LABEL[verdict]} ${r.id} (${(r.durationMs / 1000).toFixed(1)}s)`
1403
1485
  );
1404
1486
  if (r.description !== undefined) {
1405
1487
  process.stdout.write(` — ${r.description}`);
1406
1488
  }
1407
1489
  process.stdout.write("\n");
1490
+ if (r.skipReason !== undefined) {
1491
+ process.stdout.write(` skipped: ${r.skipReason}\n`);
1492
+ }
1408
1493
  for (const a of r.assertions) {
1409
- process.stdout.write(
1410
- ` ${a.passed ? "✓" : "✗"} ${a.name}${
1411
- a.detail !== undefined ? ` (${a.detail})` : ""
1412
- }\n`
1413
- );
1494
+ process.stdout.write(` ${formatEvalAssertionLine(a)}\n`);
1414
1495
  }
1415
1496
  for (const [name, value] of Object.entries(r.metrics ?? {})) {
1416
1497
  process.stdout.write(` metric ${name}=${String(value)}\n`);
@@ -1427,13 +1508,51 @@ function printEvalResults(
1427
1508
  );
1428
1509
  }
1429
1510
  }
1511
+ const extra: string[] = [];
1512
+ if (counts.scored > 0) {
1513
+ extra.push(
1514
+ `${counts.scored} scored${strict ? " (fatal: --strict)" : ""}`
1515
+ );
1516
+ }
1517
+ if (counts.skipped > 0) {
1518
+ extra.push(`${counts.skipped} skipped`);
1519
+ }
1430
1520
  process.stdout.write(
1431
- `\n${passed} passed, ${failed} failed, ${results.length} total\n`
1521
+ `\n${counts.passed} passed, ${counts.failed} failed${
1522
+ extra.length === 0 ? "" : `, ${extra.join(", ")}`
1523
+ }, ${results.length} total\n`
1432
1524
  );
1525
+ if (artifactsDir !== undefined) {
1526
+ process.stdout.write(`artifacts: ${artifactsDir}\n`);
1527
+ }
1433
1528
  }
1434
1529
  return ok ? 0 : 1;
1435
1530
  }
1436
1531
 
1532
+ /**
1533
+ * One assertion line. Soft assertions read `~` rather than `✗` when they miss,
1534
+ * so a tracked score is visibly different from a failed gate.
1535
+ */
1536
+ export function formatEvalAssertionLine(
1537
+ assertion: EvalAssertionResult
1538
+ ): string {
1539
+ const soft = (assertion.severity ?? "gate") === "soft";
1540
+ const mark = assertion.passed ? "✓" : soft ? "~" : "✗";
1541
+ const parts: string[] = [];
1542
+ if (assertion.score !== undefined) {
1543
+ parts.push(
1544
+ `score=${assertion.score.toFixed(3)}${
1545
+ assertion.threshold === undefined ? "" : ` >= ${assertion.threshold}`
1546
+ }`
1547
+ );
1548
+ }
1549
+ if (assertion.detail !== undefined) {
1550
+ parts.push(assertion.detail);
1551
+ }
1552
+ const suffix = parts.length === 0 ? "" : ` (${parts.join(", ")})`;
1553
+ return `${mark} ${assertion.name}${soft ? " [soft]" : ""}${suffix}`;
1554
+ }
1555
+
1437
1556
  /**
1438
1557
  * `logs` — follow deploy progress (`--prod`) then runtime agent logs.
1439
1558
  * Local / `--url` tails `/v1/logs` only.
@@ -29,6 +29,8 @@
29
29
  import { mkdir, readFile, writeFile } from "node:fs/promises";
30
30
  import { homedir } from "node:os";
31
31
  import { dirname, join } from "node:path";
32
+ import { resolveDeployAuth } from "./cli-deploy.js";
33
+ import { DeployApiError } from "./deploy-client.js";
32
34
  import { CLI_COMMAND_NAME as CLI, LOG_PREFIX } from "./distribution.js";
33
35
  import {
34
36
  ProdTargetError,
@@ -77,9 +79,16 @@ export interface McpCliOptions {
77
79
  print?: boolean;
78
80
  /** `install`: machine-readable output. */
79
81
  json?: boolean;
82
+ /**
83
+ * `install --prod`: write a REMOTE HTTP entry pointing at the stable Cursor
84
+ * MCP gateway (`<api>/…/mcp/<team>/<slug>`) instead of the local stdio
85
+ * proxy. For remote-only MCP hosts (Glass) that cannot spawn stdio.
86
+ */
87
+ remote?: boolean;
80
88
  /** Injected seams for tests. */
81
89
  fetchImpl?: typeof fetch;
82
90
  resolveProdTargetImpl?: typeof resolveProdTarget;
91
+ resolveDeployAuthImpl?: typeof resolveDeployAuth;
83
92
  stdin?: NodeJS.ReadableStream;
84
93
  stdout?: { write(chunk: string): unknown };
85
94
  err?: (text: string) => void;
@@ -490,10 +499,9 @@ function requestIdsOf(line: string): Array<string | number> {
490
499
  // install: write the entry into ~/.cursor/mcp.json
491
500
  // ============================================================================
492
501
 
493
- interface McpJsonEntry {
494
- command: string;
495
- args: string[];
496
- }
502
+ type McpJsonEntry =
503
+ | { command: string; args: string[] }
504
+ | { url: string; headers?: Record<string, string> };
497
505
 
498
506
  export async function cmdMcpInstall(options: McpCliOptions): Promise<number> {
499
507
  const err = options.err ?? defaultErr;
@@ -502,9 +510,16 @@ export async function cmdMcpInstall(options: McpCliOptions): Promise<number> {
502
510
  err("--prod and --url are mutually exclusive\n");
503
511
  return 1;
504
512
  }
513
+ if (options.remote === true) {
514
+ if (options.prod !== true) {
515
+ err("--remote requires --prod (the hosted deployment MCP gateway)\n");
516
+ return 1;
517
+ }
518
+ return cmdMcpInstallRemote(options);
519
+ }
505
520
 
506
521
  let name: string;
507
- let entry: McpJsonEntry;
522
+ let entry: { command: string; args: string[] };
508
523
  if (options.prod === true) {
509
524
  let slug: string;
510
525
  try {
@@ -598,6 +613,104 @@ export async function cmdMcpInstall(options: McpCliOptions): Promise<number> {
598
613
  return 0;
599
614
  }
600
615
 
616
+ /**
617
+ * `mcp install --prod --remote` — write a REMOTE HTTP entry pointing at the
618
+ * stable Cursor MCP gateway for the hosted deployment, for MCP hosts that
619
+ * cannot spawn the stdio proxy (Glass / Sand). The gateway mints/refreshes
620
+ * engineAccess server-side, so the entry never carries a pod hostname or a
621
+ * short-lived port token — only the durable, stable gateway URL.
622
+ *
623
+ * Auth: this headless/CLI entry sends the `agentkit login` API key as a
624
+ * bearer against the user-API-key gateway surface. That key is written to
625
+ * `mcp.json` in plain text (flagged below); a signed-in Cursor client
626
+ * (Glass) can instead use the session gateway surface with no header at all
627
+ * once first-party session attachment ships (see PR follow-ups).
628
+ */
629
+ async function cmdMcpInstallRemote(options: McpCliOptions): Promise<number> {
630
+ const err = options.err ?? defaultErr;
631
+ const stdout = options.stdout ?? process.stdout;
632
+
633
+ let slug: string;
634
+ try {
635
+ slug = resolveProdSlug({
636
+ dir: options.dir,
637
+ cwd: options.cwd,
638
+ slug: options.slug,
639
+ });
640
+ } catch (error) {
641
+ err(`${describeError(error)}\n`);
642
+ return 1;
643
+ }
644
+
645
+ const resolveAuthImpl = options.resolveDeployAuthImpl ?? resolveDeployAuth;
646
+ const auth = await resolveAuthImpl({
647
+ team: options.team,
648
+ apiKey: options.apiKey,
649
+ fetchImpl: options.fetchImpl,
650
+ err,
651
+ });
652
+ if (typeof auth === "number") {
653
+ return auth;
654
+ }
655
+
656
+ // Verify the deployment resolves before writing config that cannot work.
657
+ // This read is admin-tier, but calling the gateway only needs team
658
+ // membership — so a 403 means "cannot verify", not "cannot use": warn and
659
+ // still write the entry rather than locking members out of their own agent.
660
+ try {
661
+ await auth.client.getDeployment({ teamId: auth.teamId, slug });
662
+ } catch (error) {
663
+ if (error instanceof DeployApiError && error.status === 403) {
664
+ err(
665
+ `Could not verify deployment "${slug}" (reading deployments needs team-admin); writing the entry anyway.\n`
666
+ );
667
+ } else {
668
+ err(`${describeError(error)}\n`);
669
+ if (error instanceof DeployApiError && error.status === 404) {
670
+ err(`Deploy it first: ${CLI} deploy --slug ${slug}\n`);
671
+ }
672
+ return 1;
673
+ }
674
+ }
675
+
676
+ const name = resolveServerName(options.name, slug);
677
+ const url = auth.client.mcpGatewayUrl(auth.teamId, slug);
678
+ const entry: { url: string; headers?: Record<string, string> } = {
679
+ url,
680
+ headers: { Authorization: `Bearer ${auth.apiKey}` },
681
+ };
682
+
683
+ if (options.print === true) {
684
+ stdout.write(
685
+ `${JSON.stringify({ mcpServers: { [name]: entry } }, null, 2)}\n`
686
+ );
687
+ return 0;
688
+ }
689
+
690
+ const path = cursorMcpJsonPath();
691
+ try {
692
+ await upsertMcpJsonEntry(path, name, entry);
693
+ } catch (error) {
694
+ err(`${describeError(error)}\n`);
695
+ return 1;
696
+ }
697
+
698
+ err(
699
+ "note: the API key is written to mcp.json in plain text; it is a durable " +
700
+ "credential for a remote MCP host that cannot spawn the stdio proxy.\n"
701
+ );
702
+ if (options.json === true) {
703
+ stdout.write(`${JSON.stringify({ ok: true, path, name, entry })}\n`);
704
+ return 0;
705
+ }
706
+ stdout.write(
707
+ `Installed remote MCP server "${name}" in ${path}\n` +
708
+ ` ${url}\n` +
709
+ "Cursor watches this file; the server appears without a restart.\n"
710
+ );
711
+ return 0;
712
+ }
713
+
601
714
  /** `~/.cursor/mcp.json` — the user-level Cursor MCP config. */
602
715
  export function cursorMcpJsonPath(
603
716
  home: string = process.env.HOME ?? homedir()