@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
package/src/evals.ts CHANGED
@@ -44,48 +44,116 @@
44
44
  * ],
45
45
  * });
46
46
  * ```
47
+ *
48
+ * ## Severity: gates and scores
49
+ *
50
+ * Every assertion returns a chainable handle, so severity rides on the
51
+ * assertion instead of a separate thresholds map. Gates are hard (a miss fails
52
+ * the eval and `eval` exits non-zero); soft assertions are tracked scores that
53
+ * mark the eval `scored` and only fail the build under `--strict`:
54
+ *
55
+ * ```ts
56
+ * t.succeeded(); // gate (default)
57
+ * t.calledTool("get_weather").soft(); // tracked, no gate
58
+ * t.check(t.reply, similarity("Sunny, 72F")).atLeast(0.8);
59
+ * t.judge.closedQA("cites a source").atLeast(0.6); // LLM-graded
60
+ * ```
61
+ *
62
+ * Matchers narrow tool assertions (`{ input, output, status, count }` accept a
63
+ * literal, a RegExp, or a predicate), and each turn returned by `t.send(...)`
64
+ * carries the same vocabulary scoped to that turn.
47
65
  */
48
66
 
67
+ import type {
68
+ EvalAssertionResult,
69
+ EvalAssertions,
70
+ EvalVerdict,
71
+ } from "./evals/assertions.js";
72
+ import type { Expectation } from "./evals/expect.js";
73
+ import type { EvalJudge, EvalJudgeConfig } from "./evals/judge.js";
74
+ import type { EvalReporter } from "./evals/results.js";
49
75
  import { normalizePositiveInt } from "./internal/bounded-int.js";
50
76
  import { brandDefinition } from "./internal/brand.js";
51
77
  import type { AgentCloudOptions, SessionEvent } from "./types.js";
52
78
 
53
- export type Expectation =
54
- | { kind: "includes"; value: string | RegExp }
55
- | { kind: "equals"; value: unknown }
56
- | {
57
- kind: "satisfies";
58
- predicate: (value: unknown) => boolean;
59
- label: string;
60
- };
61
-
62
- export function includes(value: string | RegExp): Expectation {
63
- return { kind: "includes", value };
64
- }
65
-
66
- export function equals(value: unknown): Expectation {
67
- return { kind: "equals", value };
68
- }
69
-
70
- export function satisfies(
71
- predicate: (value: unknown) => boolean,
72
- label: string
73
- ): Expectation {
74
- return { kind: "satisfies", predicate, label };
75
- }
79
+ // Assertion severity, the scoped assertion vocabulary, value assertion
80
+ // builders, the judge surface, and result/reporter types are all part of the
81
+ // authoring surface: one import (`@cursor/july/evals`) covers a whole eval file.
82
+ export type {
83
+ EvalAssertionHandle,
84
+ EvalAssertionResult,
85
+ EvalAssertionSeverity,
86
+ EvalAssertions,
87
+ EvalVerdict,
88
+ } from "./evals/assertions.js";
89
+ export { computeVerdict, verdictFailsRun } from "./evals/assertions.js";
90
+ export type { EvalSchemaLike, Expectation } from "./evals/expect.js";
91
+ export {
92
+ equals,
93
+ includes,
94
+ matches,
95
+ normalizedSimilarity,
96
+ satisfies,
97
+ similarity,
98
+ } from "./evals/expect.js";
99
+ export type {
100
+ EvalJudge,
101
+ EvalJudgeCallOptions,
102
+ EvalJudgeConfig,
103
+ EvalJudgeGraderName,
104
+ } from "./evals/judge.js";
105
+ // A custom judge built on `t.judge.model` embeds the same attacker-controlled
106
+ // agent output the built-in graders do, so it needs the same two protections.
107
+ export {
108
+ EVAL_JUDGE_INJECTION_GUARD,
109
+ fenceUntrusted,
110
+ } from "./evals/judge.js";
111
+ export type {
112
+ EvalCountMatcher,
113
+ EvalEventMatcher,
114
+ EvalEventOrderMatcher,
115
+ EvalMatcher,
116
+ EvalSubagentMatcher,
117
+ EvalToolCallMatcher,
118
+ EvalToolCallStatus,
119
+ } from "./evals/matchers.js";
120
+ export type {
121
+ EvalReporter,
122
+ EvalReporterEvalInfo,
123
+ EvalReporterTarget,
124
+ EvalRunResult,
125
+ EvalRunSummary,
126
+ } from "./evals/results.js";
127
+ export type {
128
+ EvalInputRequestFact,
129
+ EvalRunFacts,
130
+ EvalSubagentFact,
131
+ EvalToolCallFact,
132
+ } from "./evals/run-facts.js";
133
+ export { deriveRunFacts } from "./evals/run-facts.js";
76
134
 
77
- export interface EvalAssertionResult {
78
- name: string;
79
- passed: boolean;
80
- detail?: string;
81
- }
82
-
83
- export interface EvalTurnResult {
135
+ /**
136
+ * One settled turn. Carries the same assertion vocabulary as `t`, narrowed to
137
+ * this turn's events, so an intermediate turn can be graded before the next
138
+ * `send` overwrites `t.reply`:
139
+ *
140
+ * ```ts
141
+ * const draft = await t.send("Draft the summary.");
142
+ * draft.calledTool("read_file");
143
+ * t.check(draft.message, includes("Summary"));
144
+ * ```
145
+ */
146
+ export interface EvalTurnResult extends EvalAssertions {
84
147
  message: string;
85
148
  sessionId: string;
86
149
  events: SessionEvent[];
150
+ /** Tool names requested during this turn, in order. */
87
151
  toolCalls: string[];
88
152
  ok: boolean;
153
+ /** 1-based turn index within the case. */
154
+ index: number;
155
+ /** Throws when this turn failed. Only needed when later steps depend on it. */
156
+ expectOk(): EvalTurnResult;
89
157
  }
90
158
 
91
159
  export interface EvalSendOptions {
@@ -101,7 +169,15 @@ export interface EvalSendOptions {
101
169
  cloud?: AgentCloudOptions;
102
170
  }
103
171
 
104
- export interface EvalTestContext {
172
+ /**
173
+ * The eval driver and assertion surface.
174
+ *
175
+ * `t` both drives the agent and grades it. Assertions on `t` read the whole run
176
+ * once `test` finishes; assertions on the turn returned by `t.send(...)` read
177
+ * only that turn. Recorded assertions never throw, so one run reports every
178
+ * failure rather than dying on the first.
179
+ */
180
+ export interface EvalTestContext extends EvalAssertions {
105
181
  /** Send a turn and wait until the session parks (or fails). */
106
182
  send(message: string, options?: EvalSendOptions): Promise<EvalTurnResult>;
107
183
  /** Last assistant text, or null before the first send. */
@@ -109,6 +185,8 @@ export interface EvalTestContext {
109
185
  readonly sessionId: string | undefined;
110
186
  /** All events captured so far across turns. */
111
187
  readonly events: SessionEvent[];
188
+ /** Turns settled so far, oldest first. */
189
+ readonly turns: readonly EvalTurnResult[];
112
190
  /**
113
191
  * 1-based index of this run when {@link EvalConfig.iterations} /
114
192
  * {@link EvalDatapoint.iterations} expands a datapoint. Always `1` when
@@ -117,16 +195,25 @@ export interface EvalTestContext {
117
195
  readonly iteration: number;
118
196
  /** Total iterations for this datapoint (default `1`). */
119
197
  readonly iterations: number;
120
- /** Gate: run did not fail. */
121
- succeeded(): void;
122
- /** Gate: at least one completed call to `name`. */
123
- calledTool(name: string): void;
124
- /** Gate: no call to `name`. */
125
- notCalledTool(name: string): void;
126
- /** Gate: joined assistant text matches. */
127
- messageIncludes(token: string | RegExp): void;
128
- /** Gate a value with an {@link Expectation} builder. */
129
- check(value: unknown, assertion: Expectation): void;
198
+ /** Aborts when the case hits its timeout. */
199
+ readonly signal: AbortSignal;
200
+ /**
201
+ * LLM-as-judge assertions. Soft by default (tracked, never fatal); add a bar
202
+ * with `.atLeast(0.7)` or promote with `.gate(0.8)`. Requires a judge model
203
+ * from `defineEvalConfig({ judge })`, `defineEval({ judge })`, or the call.
204
+ */
205
+ readonly judge: EvalJudge;
206
+ /**
207
+ * Record a gate that must pass before the script can safely continue.
208
+ * Returns `value` when it passes and stops the test body when it does not,
209
+ * without adding a duplicate execution error.
210
+ */
211
+ require<T>(value: T, assertion: Expectation): Promise<T>;
212
+ /**
213
+ * End the case as skipped. Skipped cases are reported separately and never
214
+ * change the exit code. Call it before sending messages.
215
+ */
216
+ skip(reason: string): never;
130
217
  /** Debug line (surfaced under `agent-serve eval --verbose`). */
131
218
  log(message: string): void;
132
219
  /**
@@ -158,6 +245,12 @@ export interface EvalDatapoint {
158
245
  * concurrent case.
159
246
  */
160
247
  iterations?: number;
248
+ /** Free-form data carried onto the result and every reporter. */
249
+ metadata?: Record<string, unknown>;
250
+ /** Judge model for this datapoint (overrides `evals.config.ts`). */
251
+ judge?: EvalJudgeConfig;
252
+ /** Destinations for just this datapoint, in addition to config reporters. */
253
+ reporters?: EvalReporter[];
161
254
  test: (t: EvalTestContext) => Promise<void>;
162
255
  }
163
256
 
@@ -174,6 +267,12 @@ export interface EvalConfig {
174
267
  * Case-level {@link EvalDatapoint.iterations} overrides this.
175
268
  */
176
269
  iterations?: number;
270
+ /** Free-form data carried onto every result in this file and to reporters. */
271
+ metadata?: Record<string, unknown>;
272
+ /** Judge model for this file (overrides `evals.config.ts`). */
273
+ judge?: EvalJudgeConfig;
274
+ /** Destinations for this file, in addition to config reporters. */
275
+ reporters?: EvalReporter[];
177
276
  /** Single-datapoint shorthand. Case id = file path under `evals/`. */
178
277
  test?: (t: EvalTestContext) => Promise<void>;
179
278
  /** Multiple datapoints in this file. Each id = `<fileId>/<case.id>`. */
@@ -189,6 +288,9 @@ export interface EvalCaseDefinition {
189
288
  iterations?: number;
190
289
  /** 1-based iteration index for this expanded runnable case. */
191
290
  iteration?: number;
291
+ metadata?: Record<string, unknown>;
292
+ judge?: EvalJudgeConfig;
293
+ reporters?: EvalReporter[];
192
294
  test: (t: EvalTestContext) => Promise<void>;
193
295
  readonly __agentServe: "eval";
194
296
  }
@@ -275,12 +377,19 @@ export function expandEvalDefinition(
275
377
  const iterations = normalizeEvalIterations(
276
378
  datapoint.iterations ?? definition.iterations ?? 1
277
379
  );
380
+ const metadata =
381
+ datapoint.metadata === undefined && definition.metadata === undefined
382
+ ? undefined
383
+ : { ...definition.metadata, ...datapoint.metadata };
278
384
  return expandIterations({
279
385
  baseId: `${fileId}/${datapoint.id}`,
280
386
  description,
281
387
  tags: datapoint.tags ?? definition.tags,
282
388
  timeoutMs,
283
389
  iterations,
390
+ metadata,
391
+ judge: datapoint.judge ?? definition.judge,
392
+ reporters: mergeReporters(definition.reporters, datapoint.reporters),
284
393
  test: datapoint.test,
285
394
  });
286
395
  });
@@ -295,16 +404,36 @@ export function expandEvalDefinition(
295
404
  tags: definition.tags,
296
405
  timeoutMs: definition.timeoutMs,
297
406
  iterations,
407
+ metadata: definition.metadata,
408
+ judge: definition.judge,
409
+ reporters: definition.reporters,
298
410
  test: definition.test,
299
411
  });
300
412
  }
301
413
 
414
+ /** Union file-level and case-level reporters, keeping each instance once. */
415
+ function mergeReporters(
416
+ fileLevel: EvalReporter[] | undefined,
417
+ caseLevel: EvalReporter[] | undefined
418
+ ): EvalReporter[] | undefined {
419
+ if (fileLevel === undefined) {
420
+ return caseLevel;
421
+ }
422
+ if (caseLevel === undefined) {
423
+ return fileLevel;
424
+ }
425
+ return [...new Set([...fileLevel, ...caseLevel])];
426
+ }
427
+
302
428
  function expandIterations(args: {
303
429
  baseId: string;
304
430
  description?: string;
305
431
  tags?: string[];
306
432
  timeoutMs?: number;
307
433
  iterations: number;
434
+ metadata?: Record<string, unknown>;
435
+ judge?: EvalJudgeConfig;
436
+ reporters?: EvalReporter[];
308
437
  test: (t: EvalTestContext) => Promise<void>;
309
438
  }): Array<{ id: string; definition: EvalCaseDefinition }> {
310
439
  const out: Array<{ id: string; definition: EvalCaseDefinition }> = [];
@@ -321,6 +450,9 @@ function expandIterations(args: {
321
450
  ...(description !== undefined ? { description } : {}),
322
451
  ...(args.tags !== undefined ? { tags: args.tags } : {}),
323
452
  ...(args.timeoutMs !== undefined ? { timeoutMs: args.timeoutMs } : {}),
453
+ metadata: args.metadata,
454
+ judge: args.judge,
455
+ reporters: args.reporters,
324
456
  iterations: args.iterations,
325
457
  iteration,
326
458
  test: args.test,
@@ -409,6 +541,10 @@ export interface EvalCaseSnapshot {
409
541
  tags?: string[];
410
542
  status: EvalCaseRunStatus;
411
543
  ok?: boolean;
544
+ /** Full grade, including `scored` (soft threshold miss) and `skipped`. */
545
+ verdict?: EvalVerdict;
546
+ /** Reason passed to `t.skip(...)`. */
547
+ skipReason?: string;
412
548
  assertions?: EvalAssertionResult[];
413
549
  sessionId?: string;
414
550
  durationMs?: number;
@@ -416,6 +552,8 @@ export interface EvalCaseSnapshot {
416
552
  logs?: string[];
417
553
  /** Structured scores from {@link EvalTestContext.metric}. */
418
554
  metrics?: Record<string, string | number>;
555
+ /** Author-supplied metadata from `defineEval({ metadata })`. */
556
+ metadata?: Record<string, unknown>;
419
557
  inputs?: string[];
420
558
  finalText?: string;
421
559
  tools?: string[];
@@ -434,7 +572,19 @@ export interface EvalRunSnapshot {
434
572
  startedAt: string;
435
573
  finishedAt?: string;
436
574
  error?: string;
437
- summary: { passed: number; failed: number; total: number; done: number };
575
+ summary: {
576
+ passed: number;
577
+ failed: number;
578
+ /**
579
+ * Cases that only missed a soft threshold (fatal only under `--strict`).
580
+ * Optional: a batch persisted by an older serve process has no such count.
581
+ */
582
+ scored?: number;
583
+ /** Cases ended by `t.skip(...)`; they never change the exit code. */
584
+ skipped?: number;
585
+ total: number;
586
+ done: number;
587
+ };
438
588
  cases: EvalCaseSnapshot[];
439
589
  config: {
440
590
  maxConcurrency?: number;
@@ -497,6 +647,17 @@ export interface EvalConfigFile {
497
647
  * request limits.
498
648
  */
499
649
  maxConcurrency: number;
650
+ /**
651
+ * Default judge model for `t.judge.*` across this tree. Optional: a tree of
652
+ * fully deterministic evals never needs one.
653
+ */
654
+ judge?: EvalJudgeConfig;
655
+ /**
656
+ * Destinations that observe **every** case in the run — the right place for a
657
+ * shared one, so you do not repeat it in each eval file. `--skip-report`
658
+ * suppresses them for a local iteration loop.
659
+ */
660
+ reporters?: EvalReporter[];
500
661
  /**
501
662
  * Optional. Max batches kept in the **playground / `/v1/dev/evals*`**
502
663
  * history window (in-memory, and pruned from the `defineStorage` evals
@@ -523,6 +684,8 @@ export function defineEvalConfig(config: EvalConfigFile): EvalConfigFile {
523
684
  maxPlaygroundRuns: normalizeEvalMaxPlaygroundRuns(
524
685
  config.maxPlaygroundRuns ?? EVAL_DEFAULT_MAX_PLAYGROUND_RUNS
525
686
  ),
687
+ judge: config.judge,
688
+ reporters: config.reporters,
526
689
  };
527
690
  }
528
691
 
package/src/index.ts CHANGED
@@ -124,12 +124,14 @@ export {
124
124
  export { defineTool } from "./tools.js";
125
125
  export type * from "./types.js";
126
126
  export {
127
+ cursorAccountServersHasMixedWildcard,
127
128
  DEFAULT_MODEL,
128
129
  DIRECT_TOOL_CALL_CHANNEL_ID,
129
130
  isCursorAccountTransport,
130
131
  isPeerConnectionTransport,
131
132
  isSymbolicConnectionTransport,
132
133
  MAX_RUNNING_TURNS_LIMIT,
134
+ normalizeCursorAccountServers,
133
135
  } from "./types.js";
134
136
 
135
137
  /**
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Built-in tools (`AgentConfig.builtinTools`): framework-provided
3
+ * model-facing tools, materialized as ordinary server tools at discovery
4
+ * time so turns, direct calls, `info`, and the playground treat them
5
+ * exactly like authored tools.
6
+ */
7
+
8
+ import type {
9
+ BuiltinToolsConfig,
10
+ DiscoveredTool,
11
+ ToolDefinition,
12
+ } from "../../types.js";
13
+ import { toolInputJsonSchema } from "../json-schema.js";
14
+ import { reminderTools } from "./reminders.js";
15
+
16
+ /** Resolve the enabled built-in tools, in stable catalog order. */
17
+ export function builtinTools(
18
+ config: BuiltinToolsConfig | undefined
19
+ ): DiscoveredTool[] {
20
+ const tools: DiscoveredTool[] = [];
21
+ if (config?.reminders === true) {
22
+ tools.push(...toDiscovered(reminderTools));
23
+ }
24
+ return tools;
25
+ }
26
+
27
+ function toDiscovered(
28
+ byName: Record<string, ToolDefinition>
29
+ ): DiscoveredTool[] {
30
+ return Object.entries(byName).map(([name, definition]) => ({
31
+ name,
32
+ description: definition.description,
33
+ inputSchema: toolInputJsonSchema(definition.inputSchema),
34
+ execution: "server",
35
+ needsApproval:
36
+ definition.needsApproval === true ||
37
+ typeof definition.needsApproval === "function",
38
+ definition,
39
+ }));
40
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Built-in reminder tools (`builtinTools: { reminders: true }`): durable
3
+ * wakes bound to the current conversation over `host.reminders`. The
4
+ * framework's reminder fire prompt tells the model to call
5
+ * `reminders_cancel` by name, so these tool names must not change.
6
+ */
7
+
8
+ import { z } from "zod";
9
+ import { defineTool } from "../../tools.js";
10
+ import type {
11
+ ReminderHostApi,
12
+ ReminderInfo,
13
+ ToolContext,
14
+ ToolDefinition,
15
+ } from "../../types.js";
16
+ import { parseDuration } from "../duration.js";
17
+
18
+ /** Floor for model-armed recurring wakes: each fire spends a model turn. */
19
+ const MIN_EVERY_MS = 60_000;
20
+
21
+ function api(ctx: ToolContext): ReminderHostApi {
22
+ const reminders = ctx.host.reminders;
23
+ if (reminders === undefined) {
24
+ throw new Error("Reminders are disabled on this host.");
25
+ }
26
+ return reminders;
27
+ }
28
+
29
+ function conversation(ctx: ToolContext): {
30
+ channelId: string;
31
+ continuationToken: string;
32
+ } {
33
+ const token = ctx.session.continuationKey;
34
+ if (token == null) {
35
+ throw new Error(
36
+ "This session is not bound to a followable conversation; reminders cannot wake it."
37
+ );
38
+ }
39
+ return { channelId: ctx.session.channelId, continuationToken: token };
40
+ }
41
+
42
+ /** Reminders visible to this conversation (tokens are channel-local). */
43
+ async function listMine(
44
+ ctx: ToolContext,
45
+ status?: ReminderInfo["status"]
46
+ ): Promise<ReminderInfo[]> {
47
+ const conv = conversation(ctx);
48
+ const all = await api(ctx).list({
49
+ status,
50
+ continuationToken: conv.continuationToken,
51
+ });
52
+ return all.filter((r) => r.channelId === conv.channelId);
53
+ }
54
+
55
+ function toResult(r: ReminderInfo) {
56
+ return {
57
+ reminderId: r.id,
58
+ purpose: r.purpose,
59
+ schedule: `${r.scheduleKind} ${r.scheduleValue}`,
60
+ nextFireAt: r.nextFireAt,
61
+ until: r.untilPrompt,
62
+ };
63
+ }
64
+
65
+ const create = defineTool({
66
+ description:
67
+ "Set a reminder for yourself in this conversation. When it fires, your prompt returns here as a follow-up turn. Use it to re-check state that needs time to settle. One-shot (delay) or recurring (every).",
68
+ inputSchema: z.object({
69
+ prompt: z
70
+ .string()
71
+ .min(1)
72
+ .describe(
73
+ "What to do when you wake. Keep it generic ('re-check CI and report only if the status changed') and re-read live state on wake instead of replaying stale details."
74
+ ),
75
+ delay: z
76
+ .string()
77
+ .optional()
78
+ .describe(
79
+ 'One-shot: wake once after this duration, e.g. "20m" or "2h". Provide exactly one of delay or every.'
80
+ ),
81
+ every: z
82
+ .string()
83
+ .optional()
84
+ .describe(
85
+ 'Recurring: wake at this interval, e.g. "30m", until cancelled or the stop condition is met. Provide exactly one of delay or every.'
86
+ ),
87
+ purpose: z
88
+ .string()
89
+ .min(1)
90
+ .max(64)
91
+ .optional()
92
+ .describe(
93
+ 'Short slug naming the reminder, e.g. "ci_recheck". Defaults to "follow_up".'
94
+ ),
95
+ until: z
96
+ .string()
97
+ .optional()
98
+ .describe(
99
+ "Recurring only: plain-language stop condition you will honor by calling reminders_cancel, e.g. 'Cancel once CI is green or the PR is merged.'"
100
+ ),
101
+ }),
102
+ async execute(input, ctx) {
103
+ if ((input.delay !== undefined) === (input.every !== undefined)) {
104
+ throw new Error("Provide exactly one of delay or every.");
105
+ }
106
+ if (
107
+ input.every !== undefined &&
108
+ parseDuration(input.every) < MIN_EVERY_MS
109
+ ) {
110
+ throw new Error('Recurring reminders must use every >= "1m".');
111
+ }
112
+ const conv = conversation(ctx);
113
+ const reminder = await api(ctx).create({
114
+ purpose: input.purpose ?? "follow_up",
115
+ channelId: conv.channelId,
116
+ continuationToken: conv.continuationToken,
117
+ prompt: input.prompt,
118
+ delay: input.delay,
119
+ every: input.every,
120
+ until: input.until,
121
+ });
122
+ return toResult(reminder);
123
+ },
124
+ });
125
+
126
+ const list = defineTool({
127
+ description:
128
+ "List the armed reminders bound to this conversation. Check before arming duplicates.",
129
+ inputSchema: z.object({}),
130
+ async execute(_input, ctx) {
131
+ return { reminders: (await listMine(ctx, "armed")).map(toResult) };
132
+ },
133
+ });
134
+
135
+ const cancel = defineTool({
136
+ description:
137
+ "Cancel one of this conversation's reminders by id, the moment its stop condition is met or it is no longer needed.",
138
+ inputSchema: z.object({
139
+ id: z
140
+ .string()
141
+ .min(1)
142
+ .describe("Reminder id from reminders_create or reminders_list."),
143
+ }),
144
+ async execute(input, ctx) {
145
+ const mine = (await listMine(ctx)).find((r) => r.id === input.id);
146
+ if (mine === undefined) {
147
+ throw new Error(`No reminder "${input.id}" in this conversation.`);
148
+ }
149
+ const cancelled = await api(ctx).cancel(input.id);
150
+ return { reminderId: input.id, status: cancelled?.status ?? "cancelled" };
151
+ },
152
+ });
153
+
154
+ export const reminderTools: Record<string, ToolDefinition> = {
155
+ reminders_create: create,
156
+ reminders_list: list,
157
+ reminders_cancel: cancel,
158
+ };