@cursor/july 0.1.25 → 0.1.27

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 (288) hide show
  1. package/dist/ab.d.ts +6 -0
  2. package/dist/ab.d.ts.map +1 -1
  3. package/dist/ab.js +1 -0
  4. package/dist/artifacts.d.ts +34 -0
  5. package/dist/artifacts.d.ts.map +1 -0
  6. package/dist/artifacts.js +57 -0
  7. package/dist/bin/agent-serve.js +31 -9
  8. package/dist/channels/github/github-channel.d.ts +1 -1
  9. package/dist/channels/github/github-channel.d.ts.map +1 -1
  10. package/dist/channels/github/github-channel.js +14 -1
  11. package/dist/channels/github/types.d.ts +6 -1
  12. package/dist/channels/github/types.d.ts.map +1 -1
  13. package/dist/channels/slack/api.d.ts +2 -1
  14. package/dist/channels/slack/api.d.ts.map +1 -1
  15. package/dist/channels/slack/api.js +1 -0
  16. package/dist/channels/slack/cursor-account.d.ts +2 -1
  17. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  18. package/dist/channels/slack/cursor-account.js +1 -1
  19. package/dist/channels/slack/dispatch.d.ts +4 -1
  20. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  21. package/dist/channels/slack/dispatch.js +3 -3
  22. package/dist/channels/slack/interactive.d.ts +2 -1
  23. package/dist/channels/slack/interactive.d.ts.map +1 -1
  24. package/dist/channels/slack/interactive.js +1 -0
  25. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  26. package/dist/channels/slack/slack-channel.js +4 -3
  27. package/dist/channels/slack/types.d.ts +6 -1
  28. package/dist/channels/slack/types.d.ts.map +1 -1
  29. package/dist/docs/404.html +1 -1
  30. package/dist/docs/ab.html +2 -2
  31. package/dist/docs/assets/{app.DYcC9FY-.js → app.BICLoJYF.js} +1 -1
  32. package/dist/docs/assets/chunks/@localSearchIndexroot.CQJytDwU.js +1 -0
  33. package/dist/docs/assets/chunks/{VPLocalSearchBox.o4N_knTV.js → VPLocalSearchBox.wN4Ko9dT.js} +1 -1
  34. package/dist/docs/assets/chunks/{theme.DQ-njyo0.js → theme.dnnTE-Co.js} +2 -2
  35. package/dist/docs/building-with-agents.html +2 -2
  36. package/dist/docs/concepts.html +2 -2
  37. package/dist/docs/deployment.html +2 -2
  38. package/dist/docs/evals.html +2 -2
  39. package/dist/docs/example-agents/approval-buddy.html +2 -2
  40. package/dist/docs/example-agents/benny.html +2 -2
  41. package/dist/docs/example-agents/bugbot.html +2 -2
  42. package/dist/docs/example-agents/codebase-wiki.html +2 -2
  43. package/dist/docs/example-agents/codeowners-review.html +2 -2
  44. package/dist/docs/example-agents/concierge.html +2 -2
  45. package/dist/docs/example-agents/fsd.html +2 -2
  46. package/dist/docs/example-agents/index.html +2 -2
  47. package/dist/docs/example-agents/knowledge-base.html +2 -2
  48. package/dist/docs/example-agents/oncall.html +2 -2
  49. package/dist/docs/example-agents/security-reviewer.html +2 -2
  50. package/dist/docs/example-agents/slack-agent.html +2 -2
  51. package/dist/docs/example-agents/weather-agent.html +2 -2
  52. package/dist/docs/guides/agent-to-agent.html +2 -2
  53. package/dist/docs/guides/cloud-runtime.html +2 -2
  54. package/dist/docs/guides/github.html +2 -2
  55. package/dist/docs/guides/human-in-the-loop.html +2 -2
  56. package/dist/docs/guides/mcp-oauth.html +2 -2
  57. package/dist/docs/guides/slack.html +2 -2
  58. package/dist/docs/guides/webhooks.html +2 -2
  59. package/dist/docs/hillclimbing.html +2 -2
  60. package/dist/docs/index.html +2 -2
  61. package/dist/docs/quickstart.html +2 -2
  62. package/dist/docs/reference/agent-config.html +2 -2
  63. package/dist/docs/reference/channels.html +2 -2
  64. package/dist/docs/reference/cli.html +2 -2
  65. package/dist/docs/reference/connections.html +2 -2
  66. package/dist/docs/reference/hooks.html +2 -2
  67. package/dist/docs/reference/http-api.html +2 -2
  68. package/dist/docs/reference/instructions.html +2 -2
  69. package/dist/docs/reference/playground.html +2 -2
  70. package/dist/docs/reference/project-layout.html +2 -2
  71. package/dist/docs/reference/prompt.html +2 -2
  72. package/dist/docs/reference/schedules.html +2 -2
  73. package/dist/docs/reference/sessions.html +2 -2
  74. package/dist/docs/reference/skills.html +2 -2
  75. package/dist/docs/reference/subagents.html +2 -2
  76. package/dist/docs/reference/tools.html +2 -2
  77. package/dist/docs/scaffolding-agents.html +2 -2
  78. package/dist/docs/storage.html +2 -2
  79. package/dist/docs/troubleshooting.html +2 -2
  80. package/dist/evals/assertions.d.ts +10 -1
  81. package/dist/evals/assertions.d.ts.map +1 -1
  82. package/dist/evals/assertions.js +12 -0
  83. package/dist/evals.d.ts +1 -1
  84. package/dist/evals.d.ts.map +1 -1
  85. package/dist/index.d.ts +1 -0
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +1 -0
  88. package/dist/internal/ab-fold.d.ts.map +1 -1
  89. package/dist/internal/ab-fold.js +4 -0
  90. package/dist/internal/ab-snapshot.d.ts.map +1 -1
  91. package/dist/internal/ab-snapshot.js +1 -0
  92. package/dist/internal/artifacts-store.d.ts +45 -0
  93. package/dist/internal/artifacts-store.d.ts.map +1 -0
  94. package/dist/internal/artifacts-store.js +250 -0
  95. package/dist/internal/builtin-tools/artifacts.d.ts +10 -0
  96. package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -0
  97. package/dist/internal/builtin-tools/artifacts.js +73 -0
  98. package/dist/internal/builtin-tools/index.d.ts +8 -1
  99. package/dist/internal/builtin-tools/index.d.ts.map +1 -1
  100. package/dist/internal/builtin-tools/index.js +15 -0
  101. package/dist/internal/cli-ax.d.ts +11 -0
  102. package/dist/internal/cli-ax.d.ts.map +1 -1
  103. package/dist/internal/cli-ax.js +80 -0
  104. package/dist/internal/cli-slack.d.ts.map +1 -1
  105. package/dist/internal/cli-slack.js +14 -6
  106. package/dist/internal/discovery.d.ts.map +1 -1
  107. package/dist/internal/discovery.js +83 -2
  108. package/dist/internal/distribution.d.ts.map +1 -1
  109. package/dist/internal/distribution.js +1 -0
  110. package/dist/internal/model-pricing.d.ts +49 -0
  111. package/dist/internal/model-pricing.d.ts.map +1 -0
  112. package/dist/internal/model-pricing.js +377 -0
  113. package/dist/internal/reminder-runner.d.ts.map +1 -1
  114. package/dist/internal/reminder-runner.js +1 -0
  115. package/dist/internal/schedule-runner.d.ts.map +1 -1
  116. package/dist/internal/schedule-runner.js +1 -0
  117. package/dist/internal/sdk-runner.d.ts.map +1 -1
  118. package/dist/internal/sdk-runner.js +9 -1
  119. package/dist/internal/server.d.ts.map +1 -1
  120. package/dist/internal/server.js +39 -0
  121. package/dist/internal/session-cost.d.ts +49 -0
  122. package/dist/internal/session-cost.d.ts.map +1 -0
  123. package/dist/internal/session-cost.js +175 -0
  124. package/dist/internal/session-engine.d.ts +35 -1
  125. package/dist/internal/session-engine.d.ts.map +1 -1
  126. package/dist/internal/session-engine.js +128 -7
  127. package/dist/internal/storage-coordinator.d.ts +15 -0
  128. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  129. package/dist/internal/storage-coordinator.js +52 -0
  130. package/dist/internal/trajectory.d.ts +9 -1
  131. package/dist/internal/trajectory.d.ts.map +1 -1
  132. package/dist/internal/trajectory.js +32 -5
  133. package/dist/playground/assets/index-BDCeW4uc.js +86 -0
  134. package/dist/playground/assets/index-Bul1WfrB.css +1 -0
  135. package/dist/playground/index.html +2 -2
  136. package/dist/storage.d.ts +4 -0
  137. package/dist/storage.d.ts.map +1 -1
  138. package/dist/storage.js +4 -0
  139. package/dist/types.d.ts +164 -1
  140. package/dist/types.d.ts.map +1 -1
  141. package/package.json +26 -26
  142. package/src/ab.ts +7 -0
  143. package/src/bin/agent-serve.ts +30 -2
  144. package/src/channels/github/github-channel.ts +19 -2
  145. package/src/channels/github/types.ts +6 -0
  146. package/src/channels/slack/api.ts +3 -1
  147. package/src/channels/slack/cursor-account.ts +3 -1
  148. package/src/channels/slack/dispatch.ts +12 -1
  149. package/src/channels/slack/interactive.ts +3 -0
  150. package/src/channels/slack/slack-channel.ts +6 -0
  151. package/src/channels/slack/types.ts +6 -0
  152. package/src/internal/ab-fold.ts +4 -0
  153. package/src/internal/ab-snapshot.ts +1 -0
  154. package/src/internal/cli-ax.ts +95 -0
  155. package/src/internal/model-pricing.ts +426 -0
  156. package/src/internal/reminder-runner.ts +1 -0
  157. package/src/internal/schedule-runner.ts +1 -0
  158. package/src/internal/sdk-runner.ts +10 -2
  159. package/src/internal/server.ts +2 -0
  160. package/src/internal/session-cost.ts +231 -0
  161. package/src/internal/session-engine.ts +32 -1
  162. package/src/internal/trajectory.ts +45 -3
  163. package/src/types.ts +42 -2
  164. package/dist/channels/github/instrument.d.ts +0 -20
  165. package/dist/channels/github/instrument.d.ts.map +0 -1
  166. package/dist/docs/assets/chunks/@localSearchIndexroot.BQTzJjR_.js +0 -1
  167. package/dist/internal/json-dir-store.d.ts +0 -32
  168. package/dist/internal/json-dir-store.d.ts.map +0 -1
  169. package/dist/internal/json-dir-store.js +0 -100
  170. package/dist/playground/assets/index-CjOQ4hN9.css +0 -1
  171. package/dist/playground/assets/index-DqXdAFGa.js +0 -85
  172. package/src/bin/agent-serve.version.test.ts +0 -64
  173. package/src/channels/github/api.test.ts +0 -64
  174. package/src/channels/github/auth.test.ts +0 -105
  175. package/src/channels/github/cursor-account.test.ts +0 -204
  176. package/src/channels/github/forward.test.ts +0 -457
  177. package/src/channels/github/github.test.ts +0 -943
  178. package/src/channels/github/replay.test.ts +0 -179
  179. package/src/channels/slack/api.post-message.test.ts +0 -148
  180. package/src/channels/slack/approvals.test.ts +0 -328
  181. package/src/channels/slack/block-actions.test.ts +0 -452
  182. package/src/channels/slack/bot-mentions.test.ts +0 -267
  183. package/src/channels/slack/channel-watch.test.ts +0 -363
  184. package/src/channels/slack/cursor-account.test.ts +0 -253
  185. package/src/channels/slack/defaults.final-post.test.ts +0 -182
  186. package/src/channels/slack/dispatch.test.ts +0 -795
  187. package/src/channels/slack/eval-directive.test.ts +0 -273
  188. package/src/channels/slack/message-body.test.ts +0 -54
  189. package/src/channels/slack/nudge-store.test.ts +0 -143
  190. package/src/channels/slack/slack.test.ts +0 -391
  191. package/src/channels/slack/stop.test.ts +0 -23
  192. package/src/channels/slack/thread-context.test.ts +0 -202
  193. package/src/evals/assertions.test.ts +0 -617
  194. package/src/evals/expect.test.ts +0 -144
  195. package/src/evals/judge.test.ts +0 -181
  196. package/src/evals/loaders.test.ts +0 -132
  197. package/src/evals/matchers.test.ts +0 -95
  198. package/src/evals/reporters.test.ts +0 -303
  199. package/src/evals/run-facts.test.ts +0 -259
  200. package/src/internal/ab-snapshot.test.ts +0 -325
  201. package/src/internal/approval-gate.test.ts +0 -49
  202. package/src/internal/approvals.integration.test.ts +0 -383
  203. package/src/internal/artifacts-store.test.ts +0 -283
  204. package/src/internal/authored-loaders.test.ts +0 -31
  205. package/src/internal/builtin-tools/artifacts.test.ts +0 -247
  206. package/src/internal/builtin-tools/reminders.test.ts +0 -201
  207. package/src/internal/channel-route-schema.test.ts +0 -294
  208. package/src/internal/chat-attach.test.ts +0 -262
  209. package/src/internal/cli-deploy.test.ts +0 -1991
  210. package/src/internal/cli-docs.test.ts +0 -161
  211. package/src/internal/cli-mcp.test.ts +0 -789
  212. package/src/internal/cli-skills.test.ts +0 -133
  213. package/src/internal/cli-slack.test.ts +0 -1654
  214. package/src/internal/cloud-merge.test.ts +0 -74
  215. package/src/internal/cron.test.ts +0 -22
  216. package/src/internal/cursor/account-mcp.test.ts +0 -807
  217. package/src/internal/cursor/backend-client.test.ts +0 -591
  218. package/src/internal/cursor/credentials.test.ts +0 -351
  219. package/src/internal/cursor/github-credentials.test.ts +0 -136
  220. package/src/internal/cursor-account-mcp-auth.test.ts +0 -310
  221. package/src/internal/cursor-account.integration.test.ts +0 -441
  222. package/src/internal/cursor-event-relay.test.ts +0 -746
  223. package/src/internal/cursor-github-credentials.integration.test.ts +0 -271
  224. package/src/internal/cursor-slack-relay.test.ts +0 -525
  225. package/src/internal/deploy-source.test.ts +0 -111
  226. package/src/internal/discovery.artifact-tool.test.ts +0 -136
  227. package/src/internal/discovery.artifacts.test.ts +0 -119
  228. package/src/internal/discovery.builtin-tools.test.ts +0 -94
  229. package/src/internal/discovery.concurrency.test.ts +0 -60
  230. package/src/internal/discovery.cursor-account.test.ts +0 -133
  231. package/src/internal/discovery.cwd.test.ts +0 -83
  232. package/src/internal/discovery.hosting.test.ts +0 -80
  233. package/src/internal/discovery.identity.test.ts +0 -44
  234. package/src/internal/docs-site.test.ts +0 -66
  235. package/src/internal/duration.test.ts +0 -29
  236. package/src/internal/eval-judge-model.test.ts +0 -187
  237. package/src/internal/eval-run-store.cancel.test.ts +0 -142
  238. package/src/internal/eval-run-store.storage.test.ts +0 -211
  239. package/src/internal/eval-runner.http.test.ts +0 -403
  240. package/src/internal/eval-runner.run.test.ts +0 -928
  241. package/src/internal/evals-client.test.ts +0 -307
  242. package/src/internal/event-mapper.test.ts +0 -243
  243. package/src/internal/github-fanout.test.ts +0 -213
  244. package/src/internal/handleAgentServeTrigger.test.ts +0 -185
  245. package/src/internal/host-kv.test.ts +0 -82
  246. package/src/internal/host-platforms.test.ts +0 -126
  247. package/src/internal/http-channel.test.ts +0 -402
  248. package/src/internal/init-project.test.ts +0 -270
  249. package/src/internal/install-cursor-skills.test.ts +0 -262
  250. package/src/internal/local-env.test.ts +0 -120
  251. package/src/internal/log-ring.test.ts +0 -31
  252. package/src/internal/logs-client.test.ts +0 -350
  253. package/src/internal/mcp-endpoint.test.ts +0 -436
  254. package/src/internal/mcp-host.test.ts +0 -298
  255. package/src/internal/mcp-oauth.test.ts +0 -148
  256. package/src/internal/net.test.ts +0 -17
  257. package/src/internal/peer-connections.test.ts +0 -128
  258. package/src/internal/peer-mcp.integration.test.ts +0 -289
  259. package/src/internal/playground/toolchain.test.ts +0 -53
  260. package/src/internal/playground-cli.test.ts +0 -187
  261. package/src/internal/playground-proxy.test.ts +0 -376
  262. package/src/internal/prompt-context.integration.test.ts +0 -232
  263. package/src/internal/prompt-context.test.ts +0 -127
  264. package/src/internal/reminder-runner.test.ts +0 -390
  265. package/src/internal/reminder-store.test.ts +0 -53
  266. package/src/internal/request-headers.test.ts +0 -27
  267. package/src/internal/resolve-prod-target.test.ts +0 -787
  268. package/src/internal/resolved-connections.test.ts +0 -295
  269. package/src/internal/router.test.ts +0 -57
  270. package/src/internal/sdk-runner.test.ts +0 -290
  271. package/src/internal/server.artifacts.test.ts +0 -303
  272. package/src/internal/session-engine.artifacts.test.ts +0 -243
  273. package/src/internal/session-engine.coalesce.test.ts +0 -169
  274. package/src/internal/session-engine.concurrency.test.ts +0 -250
  275. package/src/internal/session-engine.host-oauth-mcp.test.ts +0 -110
  276. package/src/internal/session-engine.interrupt.test.ts +0 -577
  277. package/src/internal/session-engine.storage.test.ts +0 -547
  278. package/src/internal/session-urls.test.ts +0 -28
  279. package/src/internal/sessions-client.test.ts +0 -518
  280. package/src/internal/storage-coordinator.test.ts +0 -517
  281. package/src/internal/tool-call.test.ts +0 -458
  282. package/src/internal/tool-result.test.ts +0 -52
  283. package/src/internal/trajectory.approvals.test.ts +0 -83
  284. package/src/internal/trajectory.subagents.test.ts +0 -198
  285. package/src/internal/turn-governor.test.ts +0 -137
  286. package/src/internal/update-check.test.ts +0 -485
  287. package/src/internal/workspace.test.ts +0 -207
  288. package/src/storage-backends/cursor-hosted.test.ts +0 -121
@@ -1,928 +0,0 @@
1
- /**
2
- * End-to-end coverage for the eval runner: discovery, the severity model,
3
- * judges, verdicts, and reporters. `runSession` is mocked so a case can drive
4
- * a scripted agent without a live model or an HTTP server.
5
- */
6
-
7
- import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
8
- import { tmpdir } from "node:os";
9
- import { join } from "node:path";
10
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
11
- import { EvalJudgeUnavailableError } from "../evals/judge.js";
12
- import { EVAL_ROOT_ENV } from "../evals/loaders.js";
13
- import type {
14
- EvalDefinition,
15
- EvalReporter,
16
- EvalRunResult,
17
- EvalTestContext,
18
- } from "../evals.js";
19
- import { defineEval, defineEvalConfig, equals, similarity } from "../evals.js";
20
- import type { SessionEvent } from "../types.js";
21
- import * as evalRunner from "./eval-runner.js";
22
- import * as trajectory from "./trajectory.js";
23
-
24
- const runSession = vi.hoisted(() => vi.fn());
25
-
26
- vi.mock("./run-client.js", async (importOriginal) => {
27
- const actual = await importOriginal<typeof import("./run-client.js")>();
28
- return { ...actual, runSession };
29
- });
30
-
31
- // `vi.mock` is hoisted above these imports, so the runner sees the scripted
32
- // `runSession` even though the imports are static.
33
- const { discoverEvals, expandEvalModule, runDiscoveredEvals } = evalRunner;
34
- const { buildTrajectory } = trajectory;
35
-
36
- let sequence = 0;
37
-
38
- function event(type: string, data: unknown): SessionEvent {
39
- sequence++;
40
- return {
41
- sessionId: "ses_1",
42
- seq: sequence,
43
- at: new Date(sequence * 1000).toISOString(),
44
- turnId: "turn_1",
45
- type,
46
- data,
47
- } as unknown as SessionEvent;
48
- }
49
-
50
- /** A settled turn that called `get_weather` and replied "Sunny, 72F". */
51
- function weatherEvents(reply = "Sunny, 72F"): SessionEvent[] {
52
- return [
53
- event("message.received", { text: "weather?" }),
54
- event("actions.requested", {
55
- calls: [{ callId: "c1", toolName: "get_weather", args: { city: "NYC" } }],
56
- }),
57
- event("action.result", {
58
- callId: "c1",
59
- toolName: "get_weather",
60
- output: { tempF: 72 },
61
- isError: false,
62
- }),
63
- event("message.completed", { text: reply, finishReason: "stop" }),
64
- event("turn.completed", {}),
65
- event("session.waiting", {}),
66
- ];
67
- }
68
-
69
- function scriptTurn(events: SessionEvent[]): void {
70
- runSession.mockImplementation(async () => {
71
- const trajectory = buildTrajectory(events);
72
- return {
73
- sessionId: "ses_1",
74
- continuationToken: "tok_1",
75
- baseUrl: "http://127.0.0.1:3000",
76
- events,
77
- trajectory,
78
- ok: trajectory.ok,
79
- };
80
- });
81
- }
82
-
83
- async function runCase(
84
- definition: EvalDefinition,
85
- options: {
86
- reporters?: EvalReporter[];
87
- strict?: boolean;
88
- judgeCaller?: (args: { prompt: string }) => Promise<string>;
89
- judgeModel?: string;
90
- skipReport?: boolean;
91
- configReporters?: EvalReporter[];
92
- } = {}
93
- ): Promise<EvalRunResult[]> {
94
- const expanded = expandEvalModule("smoke", definition);
95
- return runDiscoveredEvals({
96
- projectRoot: "/unused",
97
- baseUrl: "http://127.0.0.1:3000",
98
- reporters: options.reporters,
99
- skipReport: options.skipReport,
100
- strict: options.strict,
101
- judgeCaller: options.judgeCaller as never,
102
- discovered: {
103
- evals: expanded.map((e) => ({
104
- id: e.id,
105
- fileId: "smoke",
106
- path: "/repo/evals/smoke.eval.ts",
107
- definition: e.definition,
108
- })),
109
- config: {
110
- maxConcurrency: 4,
111
- judge:
112
- options.judgeModel === undefined
113
- ? undefined
114
- : { model: options.judgeModel },
115
- reporters: options.configReporters,
116
- },
117
- },
118
- });
119
- }
120
-
121
- beforeEach(() => {
122
- sequence = 0;
123
- runSession.mockReset();
124
- scriptTurn(weatherEvents());
125
- });
126
-
127
- describe("verdicts", () => {
128
- it("passes a case whose gates all pass", async () => {
129
- const [result] = await runCase(
130
- defineEval({
131
- async test(t) {
132
- await t.send("weather?");
133
- t.succeeded();
134
- t.calledTool("get_weather", { input: { city: "NYC" }, count: 1 });
135
- t.messageIncludes("Sunny");
136
- },
137
- })
138
- );
139
- expect(result).toMatchObject({ verdict: "passed", ok: true });
140
- expect(result?.assertions.every((a) => a.passed)).toBe(true);
141
- });
142
-
143
- it("fails a case with a missed gate and still records every assertion", async () => {
144
- const [result] = await runCase(
145
- defineEval({
146
- async test(t) {
147
- await t.send("weather?");
148
- t.calledTool("send_email");
149
- t.messageIncludes("Rainy");
150
- t.succeeded();
151
- },
152
- })
153
- );
154
- expect(result).toMatchObject({ verdict: "failed", ok: false });
155
- // Recorded assertions never throw, so one run reports all three outcomes.
156
- expect(result?.assertions.map((a) => a.passed)).toEqual([
157
- false,
158
- false,
159
- true,
160
- ]);
161
- });
162
-
163
- it("scores a case that only misses a soft threshold, leaving ok true", async () => {
164
- const [result] = await runCase(
165
- defineEval({
166
- async test(t) {
167
- await t.send("weather?");
168
- t.succeeded();
169
- t.check(t.reply, similarity("Rainy and cold")).atLeast(0.9);
170
- },
171
- })
172
- );
173
- expect(result).toMatchObject({ verdict: "scored", ok: true });
174
- expect(result?.assertions[1]).toMatchObject({
175
- severity: "soft",
176
- threshold: 0.9,
177
- passed: false,
178
- });
179
- });
180
-
181
- it("keeps the historical fallback when a case records no assertions", async () => {
182
- const [passing] = await runCase(
183
- defineEval({
184
- async test(t) {
185
- await t.send("weather?");
186
- },
187
- })
188
- );
189
- expect(passing?.verdict).toBe("passed");
190
-
191
- scriptTurn([
192
- event("message.received", { text: "hi" }),
193
- event("turn.failed", { message: "model error" }),
194
- ]);
195
- const [failing] = await runCase(
196
- defineEval({
197
- async test(t) {
198
- await t.send("hi");
199
- },
200
- })
201
- );
202
- expect(failing?.verdict).toBe("failed");
203
- });
204
-
205
- it("reports a thrown test body as a failed execution error", async () => {
206
- const [result] = await runCase(
207
- defineEval({
208
- async test(t) {
209
- await t.send("weather?");
210
- t.succeeded();
211
- throw new Error("helper blew up");
212
- },
213
- })
214
- );
215
- expect(result).toMatchObject({ verdict: "failed", ok: false });
216
- expect(result?.error).toBe("helper blew up");
217
- // Assertions recorded before the throw are still reported.
218
- expect(result?.assertions).toHaveLength(1);
219
- });
220
- });
221
-
222
- describe("t.skip and t.require", () => {
223
- it("reports t.skip separately and keeps ok true", async () => {
224
- const [result] = await runCase(
225
- defineEval({
226
- async test(t) {
227
- t.skip("cloud runtime not available here");
228
- await t.send("weather?");
229
- },
230
- })
231
- );
232
- expect(result).toMatchObject({
233
- verdict: "skipped",
234
- ok: true,
235
- skipReason: "cloud runtime not available here",
236
- });
237
- expect(runSession).not.toHaveBeenCalled();
238
- });
239
-
240
- it("stops the body on a failed require without a duplicate execution error", async () => {
241
- const seen: string[] = [];
242
- const [result] = await runCase(
243
- defineEval({
244
- async test(t) {
245
- await t.send("weather?");
246
- await t.require(t.reply, equals("Rainy"));
247
- seen.push("unreachable");
248
- t.succeeded();
249
- },
250
- })
251
- );
252
- expect(seen).toEqual([]);
253
- expect(result).toMatchObject({ verdict: "failed", ok: false });
254
- // The recorded gate is the whole story; no duplicate execution error.
255
- expect(result?.error).toBeUndefined();
256
- expect(result?.assertions).toHaveLength(1);
257
- expect(result?.assertions[0]).toMatchObject({
258
- name: "require(equals)",
259
- passed: false,
260
- detail: "got Sunny, 72F",
261
- });
262
- });
263
-
264
- it("returns the value and continues when require passes", async () => {
265
- const [result] = await runCase(
266
- defineEval({
267
- async test(t) {
268
- await t.send("weather?");
269
- const reply = await t.require(t.reply, equals("Sunny, 72F"));
270
- t.check(reply, equals("Sunny, 72F"));
271
- },
272
- })
273
- );
274
- expect(result?.verdict).toBe("passed");
275
- expect(result?.assertions).toHaveLength(2);
276
- });
277
- });
278
-
279
- describe("turn scoping", () => {
280
- it("grades an intermediate turn before the next send overwrites the reply", async () => {
281
- const first = weatherEvents("Draft: Best regards");
282
- const second = [
283
- event("message.received", { text: "send it" }),
284
- event("actions.requested", {
285
- calls: [{ callId: "c2", toolName: "send_email", args: {} }],
286
- }),
287
- event("action.result", {
288
- callId: "c2",
289
- toolName: "send_email",
290
- output: "sent",
291
- isError: false,
292
- }),
293
- event("message.completed", { text: "Sent.", finishReason: "stop" }),
294
- event("turn.completed", {}),
295
- ];
296
- const scripted = [first, second];
297
- runSession.mockImplementation(async () => {
298
- const events = scripted.shift() ?? [];
299
- const trajectory = buildTrajectory(events);
300
- return {
301
- sessionId: "ses_1",
302
- continuationToken: "tok",
303
- baseUrl: "http://127.0.0.1:3000",
304
- events,
305
- trajectory,
306
- ok: trajectory.ok,
307
- };
308
- });
309
-
310
- const [result] = await runCase(
311
- defineEval({
312
- async test(t) {
313
- const draft = await t.send("draft it");
314
- draft.messageIncludes("Best regards");
315
- draft.notCalledTool("send_email");
316
-
317
- await t.send("send it");
318
- t.calledTool("send_email");
319
- expect(t.turns).toHaveLength(2);
320
- expect(t.reply).toBe("Sent.");
321
- },
322
- })
323
- );
324
- expect(result?.verdict).toBe("passed");
325
- expect(result?.assertions.map((a) => a.name)).toEqual([
326
- "turn[1].messageIncludes(Best regards)",
327
- "turn[1].notCalledTool(send_email)",
328
- "calledTool(send_email)",
329
- ]);
330
- expect(result?.inputs).toEqual(["draft it", "send it"]);
331
- });
332
- });
333
-
334
- describe("judges", () => {
335
- const judgeReply = "CHOICE: Y\nREASON: it names a source";
336
-
337
- it("records a judge score as a soft assertion", async () => {
338
- const judgeCaller = vi.fn(async () => judgeReply);
339
- const [result] = await runCase(
340
- defineEval({
341
- async test(t) {
342
- await t.send("weather?");
343
- t.judge.closedQA("mentions the temperature");
344
- },
345
- }),
346
- { judgeCaller, judgeModel: "gpt-5.4-mini" }
347
- );
348
- expect(result?.verdict).toBe("passed");
349
- expect(result?.assertions[0]).toMatchObject({
350
- name: "judge.closedQA(mentions the temperature)",
351
- severity: "soft",
352
- score: 1,
353
- passed: true,
354
- detail: "Y (1.00) — it names a source",
355
- });
356
- expect(judgeCaller).toHaveBeenCalledTimes(1);
357
- });
358
-
359
- it("grades t.reply by default and an explicit value with `on`", async () => {
360
- const prompts: string[] = [];
361
- await runCase(
362
- defineEval({
363
- async test(t) {
364
- await t.send("weather?");
365
- t.judge.closedQA("criterion");
366
- t.judge.closedQA("criterion", { on: "a different value" });
367
- },
368
- }),
369
- {
370
- judgeModel: "gpt-5.4-mini",
371
- judgeCaller: async ({ prompt }) => {
372
- prompts.push(prompt);
373
- return judgeReply;
374
- },
375
- }
376
- );
377
- expect(prompts[0]).toContain("Sunny, 72F");
378
- expect(prompts[1]).toContain("a different value");
379
- });
380
-
381
- it("marks the eval scored when a judge misses its bar", async () => {
382
- const [result] = await runCase(
383
- defineEval({
384
- async test(t) {
385
- await t.send("weather?");
386
- t.succeeded();
387
- t.judge.factuality("Rainy and 40F").atLeast(0.8);
388
- },
389
- }),
390
- {
391
- judgeModel: "gpt-5.4-mini",
392
- judgeCaller: async () => "CHOICE: D\nREASON: the values disagree",
393
- }
394
- );
395
- expect(result?.verdict).toBe("scored");
396
- expect(result?.assertions[1]).toMatchObject({ score: 0, threshold: 0.8 });
397
- });
398
-
399
- it("fails the eval when a judge is promoted to a gate and misses", async () => {
400
- const [result] = await runCase(
401
- defineEval({
402
- async test(t) {
403
- await t.send("weather?");
404
- t.judge.factuality("Rainy and 40F").gate(0.8);
405
- },
406
- }),
407
- { judgeModel: "gpt-5.4-mini", judgeCaller: async () => "CHOICE: D" }
408
- );
409
- expect(result?.verdict).toBe("failed");
410
- });
411
-
412
- it("records a failed gate when no judge model is configured anywhere", async () => {
413
- const judgeCaller = vi.fn(async () => judgeReply);
414
- const [result] = await runCase(
415
- defineEval({
416
- async test(t) {
417
- await t.send("weather?");
418
- t.judge.closedQA("criterion");
419
- },
420
- }),
421
- { judgeCaller }
422
- );
423
- expect(result?.verdict).toBe("failed");
424
- expect(result?.assertions[0]).toMatchObject({
425
- passed: false,
426
- detail: expect.stringContaining("no judge model configured"),
427
- });
428
- expect(judgeCaller).not.toHaveBeenCalled();
429
- });
430
-
431
- it("prefers the per-eval judge model over the config default", async () => {
432
- const models: unknown[] = [];
433
- await runCase(
434
- defineEval({
435
- judge: { model: "claude-opus-4.8" },
436
- async test(t) {
437
- await t.send("weather?");
438
- t.judge.closedQA("criterion");
439
- t.judge.closedQA("criterion", { model: "gpt-5.4-mini" });
440
- },
441
- }),
442
- {
443
- judgeModel: "config-default",
444
- judgeCaller: async (args: { prompt: string; model?: unknown }) => {
445
- models.push(args.model);
446
- return judgeReply;
447
- },
448
- }
449
- );
450
- expect(models).toEqual(["claude-opus-4.8", "gpt-5.4-mini"]);
451
- });
452
-
453
- it("skips the eval visibly when the judge has no credentials", async () => {
454
- const [result] = await runCase(
455
- defineEval({
456
- async test(t) {
457
- await t.send("weather?");
458
- t.succeeded();
459
- t.judge.closedQA("criterion");
460
- },
461
- }),
462
- {
463
- judgeModel: "gpt-5.4-mini",
464
- judgeCaller: async () => {
465
- throw new EvalJudgeUnavailableError("no Cursor credentials");
466
- },
467
- }
468
- );
469
- // A missing credential is an environment gap, not a quality regression.
470
- expect(result).toMatchObject({ verdict: "skipped", ok: true });
471
- expect(result?.skipReason).toContain("no Cursor credentials");
472
- expect(result?.logs).toContain("judge skipped: no Cursor credentials");
473
- });
474
-
475
- it("still reports a failed gate when the judge is unavailable", async () => {
476
- // Otherwise a machine without judge credentials would turn a genuinely red
477
- // eval green: the skip would bury the deterministic failure.
478
- const [result] = await runCase(
479
- defineEval({
480
- async test(t) {
481
- await t.send("weather?");
482
- t.calledTool("send_email");
483
- t.judge.closedQA("criterion");
484
- },
485
- }),
486
- {
487
- judgeModel: "gpt-5.4-mini",
488
- judgeCaller: async () => {
489
- throw new EvalJudgeUnavailableError("no Cursor credentials");
490
- },
491
- }
492
- );
493
- expect(result).toMatchObject({ verdict: "failed", ok: false });
494
- expect(result?.skipReason).toBeUndefined();
495
- expect(result?.logs.join("\n")).toContain("a gate already failed");
496
- });
497
-
498
- it("skips rather than scoring when only soft judge signal is missing", async () => {
499
- const [result] = await runCase(
500
- defineEval({
501
- async test(t) {
502
- await t.send("weather?");
503
- t.succeeded();
504
- t.judge.closedQA("criterion").atLeast(0.6);
505
- },
506
- }),
507
- {
508
- judgeModel: "gpt-5.4-mini",
509
- judgeCaller: async () => {
510
- throw new EvalJudgeUnavailableError("no Cursor credentials");
511
- },
512
- }
513
- );
514
- expect(result).toMatchObject({ verdict: "skipped", ok: true });
515
- });
516
-
517
- it("fails the assertion when the judge reply is unparseable", async () => {
518
- const [result] = await runCase(
519
- defineEval({
520
- async test(t) {
521
- await t.send("weather?");
522
- t.judge.closedQA("criterion");
523
- },
524
- }),
525
- { judgeModel: "gpt-5.4-mini", judgeCaller: async () => "I refuse" }
526
- );
527
- expect(result?.verdict).toBe("failed");
528
- expect(result?.assertions[0]?.detail).toMatch(/had no choice/);
529
- });
530
-
531
- it("runs several judges concurrently and waits for all of them", async () => {
532
- let inFlight = 0;
533
- let maxInFlight = 0;
534
- const [result] = await runCase(
535
- defineEval({
536
- async test(t) {
537
- await t.send("weather?");
538
- t.judge.closedQA("first");
539
- t.judge.closedQA("second");
540
- t.judge.closedQA("third");
541
- },
542
- }),
543
- {
544
- judgeModel: "gpt-5.4-mini",
545
- judgeCaller: async () => {
546
- inFlight++;
547
- maxInFlight = Math.max(maxInFlight, inFlight);
548
- await new Promise((resolve) => setTimeout(resolve, 5));
549
- inFlight--;
550
- return judgeReply;
551
- },
552
- }
553
- );
554
- expect(maxInFlight).toBeGreaterThan(1);
555
- expect(result?.assertions.map((a) => a.score)).toEqual([1, 1, 1]);
556
- });
557
- });
558
-
559
- describe("t.judge.model", () => {
560
- it("gives a custom judge the configured model and credentials", async () => {
561
- const seen: Array<{ prompt: string; model?: unknown }> = [];
562
- const [result] = await runCase(
563
- defineEval({
564
- async test(t) {
565
- await t.send("weather?");
566
- // The shape a domain judge uses: one raw call, then score the parse.
567
- const reply = await t.judge.model("Grade these findings: [...]");
568
- t.score("recall", JSON.parse(reply).recall as number).atLeast(0.6);
569
- },
570
- }),
571
- {
572
- judgeModel: "gpt-5.4-mini",
573
- judgeCaller: async (args: { prompt: string; model?: unknown }) => {
574
- seen.push(args);
575
- return '{"recall":0.8}';
576
- },
577
- }
578
- );
579
- expect(seen).toEqual([
580
- { prompt: "Grade these findings: [...]", model: "gpt-5.4-mini" },
581
- ]);
582
- expect(result?.verdict).toBe("passed");
583
- expect(result?.assertions[0]).toMatchObject({
584
- name: "score(recall)",
585
- score: 0.8,
586
- threshold: 0.6,
587
- passed: true,
588
- });
589
- });
590
-
591
- it("throws into the test body when no judge model is configured", async () => {
592
- const [result] = await runCase(
593
- defineEval({
594
- async test(t) {
595
- await t.send("weather?");
596
- await t.judge.model("grade this");
597
- },
598
- })
599
- );
600
- expect(result?.verdict).toBe("failed");
601
- expect(result?.error).toMatch(/no judge model configured/);
602
- });
603
- });
604
-
605
- describe("reporters", () => {
606
- function recordingReporter(): {
607
- reporter: EvalReporter;
608
- calls: string[];
609
- } {
610
- const calls: string[] = [];
611
- return {
612
- calls,
613
- reporter: {
614
- onRunStart: (evals, target) => {
615
- calls.push(
616
- `start:${evals.map((e) => e.id).join(",")}:${target.mode}`
617
- );
618
- },
619
- onEvalComplete: (result) => {
620
- calls.push(`case:${result.id}:${result.verdict}`);
621
- },
622
- onRunComplete: (summary) => {
623
- calls.push(
624
- `complete:${summary.passed}p/${summary.failed}f/${summary.scored}s`
625
- );
626
- },
627
- },
628
- };
629
- }
630
-
631
- const twoCases = defineEval({
632
- cases: [
633
- {
634
- id: "good",
635
- async test(t) {
636
- await t.send("weather?");
637
- t.succeeded();
638
- },
639
- },
640
- {
641
- id: "bad",
642
- async test(t) {
643
- await t.send("weather?");
644
- t.calledTool("missing");
645
- },
646
- },
647
- ],
648
- });
649
-
650
- it("drives the full lifecycle for a CLI reporter", async () => {
651
- const { reporter, calls } = recordingReporter();
652
- await runCase(twoCases, { reporters: [reporter] });
653
- expect(calls).toEqual([
654
- "start:smoke/good,smoke/bad:local",
655
- "case:smoke/good:passed",
656
- "case:smoke/bad:failed",
657
- "complete:1p/1f/0s",
658
- ]);
659
- });
660
-
661
- it("observes every case from a config reporter", async () => {
662
- const { reporter, calls } = recordingReporter();
663
- await runCase(twoCases, { configReporters: [reporter] });
664
- expect(calls.filter((c) => c.startsWith("case:"))).toEqual([
665
- "case:smoke/good:passed",
666
- "case:smoke/bad:failed",
667
- ]);
668
- });
669
-
670
- it("scopes a per-eval reporter to its own case", async () => {
671
- const { reporter, calls } = recordingReporter();
672
- await runCase(
673
- defineEval({
674
- cases: [
675
- {
676
- id: "watched",
677
- reporters: [reporter],
678
- async test(t) {
679
- await t.send("weather?");
680
- t.succeeded();
681
- },
682
- },
683
- {
684
- id: "unwatched",
685
- async test(t) {
686
- await t.send("weather?");
687
- t.succeeded();
688
- },
689
- },
690
- ],
691
- })
692
- );
693
- // Run hooks still fire, but only the referencing case reports a result.
694
- expect(calls).toEqual([
695
- "start:smoke/watched,smoke/unwatched:local",
696
- "case:smoke/watched:passed",
697
- "complete:2p/0f/0s",
698
- ]);
699
- });
700
-
701
- it("suppresses config and per-eval reporters under skipReport", async () => {
702
- const config = recordingReporter();
703
- const perEval = recordingReporter();
704
- const cli = recordingReporter();
705
- await runCase(
706
- defineEval({
707
- reporters: [perEval.reporter],
708
- async test(t) {
709
- await t.send("weather?");
710
- t.succeeded();
711
- },
712
- }),
713
- {
714
- configReporters: [config.reporter],
715
- reporters: [cli.reporter],
716
- skipReport: true,
717
- }
718
- );
719
- expect(config.calls).toEqual([]);
720
- expect(perEval.calls).toEqual([]);
721
- // CLI-attached reporters are explicit, so they still run.
722
- expect(cli.calls).toContain("case:smoke:passed");
723
- });
724
-
725
- it("reports strict mode on the summary so reporters can gate on it", async () => {
726
- let sawStrict: boolean | undefined;
727
- await runCase(
728
- defineEval({
729
- async test(t) {
730
- await t.send("weather?");
731
- t.succeeded();
732
- },
733
- }),
734
- {
735
- strict: true,
736
- reporters: [
737
- {
738
- onRunComplete: (summary) => {
739
- sawStrict = summary.strict;
740
- },
741
- },
742
- ],
743
- }
744
- );
745
- expect(sawStrict).toBe(true);
746
- });
747
-
748
- it("keeps running when a reporter throws", async () => {
749
- const results = await runCase(twoCases, {
750
- reporters: [
751
- {
752
- onEvalComplete: () => {
753
- throw new Error("upload failed");
754
- },
755
- },
756
- ],
757
- });
758
- expect(results.map((r) => r.verdict)).toEqual(["passed", "failed"]);
759
- });
760
- });
761
-
762
- describe("metadata and metrics", () => {
763
- it("carries eval metadata and tags onto the result", async () => {
764
- const [result] = await runCase(
765
- defineEval({
766
- tags: ["smoke"],
767
- metadata: { owner: "agents", suite: "weather" },
768
- async test(t) {
769
- await t.send("weather?");
770
- t.succeeded();
771
- t.metric("recall", "40.0% (4/10)");
772
- },
773
- })
774
- );
775
- expect(result).toMatchObject({
776
- tags: ["smoke"],
777
- metadata: { owner: "agents", suite: "weather" },
778
- metrics: { recall: "40.0% (4/10)" },
779
- });
780
- });
781
-
782
- it("lets a case-level metadata key override the file-level one", async () => {
783
- const [result] = await runCase(
784
- defineEval({
785
- metadata: { owner: "agents", suite: "weather" },
786
- cases: [
787
- {
788
- id: "override",
789
- metadata: { owner: "security" },
790
- async test(t) {
791
- await t.send("weather?");
792
- t.succeeded();
793
- },
794
- },
795
- ],
796
- })
797
- );
798
- expect(result?.metadata).toEqual({ owner: "security", suite: "weather" });
799
- });
800
- });
801
-
802
- describe("expandEvalModule", () => {
803
- it("expands a single defineEval to the file id", () => {
804
- const expanded = expandEvalModule(
805
- "weather",
806
- defineEval({ test: async () => undefined })
807
- );
808
- expect(expanded.map((e) => e.id)).toEqual(["weather"]);
809
- });
810
-
811
- it("fans a default-exported array out with zero-padded ids", () => {
812
- const rows = ["a", "b", "c"];
813
- const expanded = expandEvalModule(
814
- "sql",
815
- rows.map((row) =>
816
- defineEval({ description: row, test: async () => undefined })
817
- ) as never
818
- );
819
- expect(expanded.map((e) => e.id)).toEqual([
820
- "sql/0000",
821
- "sql/0001",
822
- "sql/0002",
823
- ]);
824
- expect(expanded[1]?.definition.description).toBe("b");
825
- });
826
-
827
- it("rejects a non-eval default export", () => {
828
- expect(() => expandEvalModule("x", { nope: true })).toThrow(
829
- /must be defineEval\(\{ \.\.\. \}\) or an array of them/
830
- );
831
- expect(() => expandEvalModule("x", [])).toThrow(/array is empty/);
832
- expect(() => expandEvalModule("x", [{ nope: true }])).toThrow(
833
- /array entry 0 must be defineEval/
834
- );
835
- });
836
- });
837
-
838
- describe("discoverEvals", () => {
839
- let projectRoot: string;
840
-
841
- beforeEach(async () => {
842
- projectRoot = await mkdtemp(join(tmpdir(), "agentkit-eval-discovery-"));
843
- await mkdir(join(projectRoot, "evals", "weather"), { recursive: true });
844
- });
845
-
846
- afterEach(async () => {
847
- await rm(projectRoot, { recursive: true, force: true });
848
- });
849
-
850
- /** Map on-disk eval paths to in-memory definitions. */
851
- function importer(
852
- modules: Record<string, unknown>
853
- ): (path: string) => Promise<Record<string, unknown>> {
854
- return async (absolutePath: string) => {
855
- for (const [suffix, mod] of Object.entries(modules)) {
856
- if (absolutePath.endsWith(suffix)) {
857
- return { default: mod };
858
- }
859
- }
860
- throw new Error(`unexpected import: ${absolutePath}`);
861
- };
862
- }
863
-
864
- it("derives ids from paths and loads the config", async () => {
865
- await writeFile(join(projectRoot, "evals/evals.config.ts"), "", "utf8");
866
- await writeFile(join(projectRoot, "evals/smoke.eval.ts"), "", "utf8");
867
- await writeFile(join(projectRoot, "evals/weather/nyc.eval.ts"), "", "utf8");
868
- // A sibling helper that is not an eval file must be ignored.
869
- await writeFile(join(projectRoot, "evals/weather/shared.ts"), "", "utf8");
870
-
871
- const { evals, config } = await discoverEvals(
872
- projectRoot,
873
- importer({
874
- "evals.config.ts": defineEvalConfig({ maxConcurrency: 7 }),
875
- "smoke.eval.ts": defineEval({ test: async () => undefined }),
876
- "nyc.eval.ts": defineEval({ test: async () => undefined }),
877
- })
878
- );
879
- expect(evals.map((e) => e.id)).toEqual(["smoke", "weather/nyc"]);
880
- expect(config.maxConcurrency).toBe(7);
881
- });
882
-
883
- it("points fixture loaders at the app root", async () => {
884
- await writeFile(join(projectRoot, "evals/evals.config.ts"), "", "utf8");
885
- await discoverEvals(
886
- projectRoot,
887
- importer({ "evals.config.ts": defineEvalConfig({ maxConcurrency: 1 }) })
888
- );
889
- expect(process.env[EVAL_ROOT_ENV]).toBe(projectRoot);
890
- });
891
-
892
- it("prefixes the failing file on a bad default export", async () => {
893
- await writeFile(join(projectRoot, "evals/broken.eval.ts"), "", "utf8");
894
- await expect(
895
- discoverEvals(projectRoot, importer({ "broken.eval.ts": { nope: true } }))
896
- ).rejects.toThrow(/broken\.eval\.ts: default export must be defineEval/);
897
- });
898
- });
899
-
900
- describe("cancellation", () => {
901
- it("returns a cancelled result without driving the agent", async () => {
902
- const controller = new AbortController();
903
- controller.abort();
904
- const results = await runDiscoveredEvals({
905
- projectRoot: "/unused",
906
- baseUrl: "http://127.0.0.1:3000",
907
- signal: controller.signal,
908
- discovered: {
909
- evals: expandEvalModule(
910
- "smoke",
911
- defineEval({
912
- async test(t: EvalTestContext) {
913
- await t.send("weather?");
914
- },
915
- })
916
- ).map((e) => ({
917
- id: e.id,
918
- fileId: "smoke",
919
- path: "/repo/evals/smoke.eval.ts",
920
- definition: e.definition,
921
- })),
922
- config: { maxConcurrency: 1 },
923
- },
924
- });
925
- expect(results).toEqual([]);
926
- expect(runSession).not.toHaveBeenCalled();
927
- });
928
- });