@cursor/july 0.1.6 → 0.1.7
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.
- package/dist/bin/agent-serve.js +42 -17
- package/dist/channels/slack/post-update-delivery.d.ts +85 -0
- package/dist/channels/slack/post-update-delivery.d.ts.map +1 -0
- package/dist/docs/404.html +1 -1
- package/dist/docs/ab.html +2 -2
- package/dist/docs/assets/{app.DMboGKUD.js → app.COTN7wgo.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B7UcKvIn.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DkEqEnJU.js → VPLocalSearchBox.BW3TBdT0.js} +1 -1
- package/dist/docs/assets/chunks/{theme.ASugdMNm.js → theme.BEJW0vE7.js} +2 -2
- package/dist/docs/building-with-agents.html +2 -2
- package/dist/docs/concepts.html +2 -2
- package/dist/docs/deployment.html +2 -2
- package/dist/docs/evals.html +2 -2
- package/dist/docs/example-agents/approval-buddy.html +2 -2
- package/dist/docs/example-agents/benny.html +2 -2
- package/dist/docs/example-agents/bugbot.html +2 -2
- package/dist/docs/example-agents/codebase-wiki.html +2 -2
- package/dist/docs/example-agents/codeowners-review.html +2 -2
- package/dist/docs/example-agents/concierge.html +2 -2
- package/dist/docs/example-agents/fsd.html +2 -2
- package/dist/docs/example-agents/index.html +2 -2
- package/dist/docs/example-agents/knowledge-base.html +2 -2
- package/dist/docs/example-agents/security-reviewer.html +2 -2
- package/dist/docs/example-agents/slack-agent.html +2 -2
- package/dist/docs/example-agents/weather-agent.html +2 -2
- package/dist/docs/guides/agent-to-agent.html +2 -2
- package/dist/docs/guides/cloud-runtime.html +2 -2
- package/dist/docs/guides/github.html +2 -2
- package/dist/docs/guides/human-in-the-loop.html +2 -2
- package/dist/docs/guides/slack.html +2 -2
- package/dist/docs/guides/webhooks.html +2 -2
- package/dist/docs/hillclimbing.html +2 -2
- package/dist/docs/index.html +2 -2
- package/dist/docs/quickstart.html +2 -2
- package/dist/docs/reference/agent-config.html +2 -2
- package/dist/docs/reference/channels.html +2 -2
- package/dist/docs/reference/cli.html +2 -2
- package/dist/docs/reference/connections.html +2 -2
- package/dist/docs/reference/hooks.html +2 -2
- package/dist/docs/reference/http-api.html +2 -2
- package/dist/docs/reference/instructions.html +2 -2
- package/dist/docs/reference/playground.html +2 -2
- package/dist/docs/reference/project-layout.html +2 -2
- package/dist/docs/reference/schedules.html +2 -2
- package/dist/docs/reference/sessions.html +2 -2
- package/dist/docs/reference/skills.html +2 -2
- package/dist/docs/reference/subagents.html +2 -2
- package/dist/docs/reference/tools.html +2 -2
- package/dist/docs/scaffolding-agents.html +2 -2
- package/dist/docs/storage.html +2 -2
- package/dist/docs/troubleshooting.html +2 -2
- package/dist/evals.d.ts +2 -2
- package/dist/internal/cli-ax.d.ts +23 -10
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +358 -55
- package/dist/internal/eval-run-store.d.ts +2 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +4 -4
- package/dist/internal/evals-client.d.ts +96 -0
- package/dist/internal/evals-client.d.ts.map +1 -0
- package/dist/internal/evals-client.js +262 -0
- package/dist/internal/init-project.js +3 -3
- package/dist/internal/json-dir-store.d.ts +32 -0
- package/dist/internal/json-dir-store.d.ts.map +1 -0
- package/dist/internal/persistence-coordinator.d.ts +127 -0
- package/dist/internal/persistence-coordinator.d.ts.map +1 -0
- package/dist/internal/server.js +11 -2
- package/dist/persistence.d.ts +184 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/playground/assets/index-72vCOBWO.js +86 -0
- package/dist/playground/assets/index-BjnMwYoR.css +1 -0
- package/dist/playground/index.html +2 -2
- package/package.json +1 -1
- package/src/bin/agent-serve.ts +47 -16
- package/src/evals.ts +2 -2
- package/src/internal/cli-ax.ts +466 -75
- package/src/internal/eval-run-store.ts +5 -0
- package/src/internal/evals-client.ts +431 -0
- package/src/internal/init-project.ts +3 -3
- package/src/internal/server.ts +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.O6eP7QZr.js +0 -1
- package/dist/playground/assets/index-7GJIUFQj.css +0 -1
- package/dist/playground/assets/index-mux9aj5P.js +0 -319
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for playground eval batches (`/v1/dev/evals/runs`).
|
|
3
|
+
*
|
|
4
|
+
* Used by `agentkit eval --prod` (or `--url`) so the batch runs on the serve
|
|
5
|
+
* process and is mirrored to `defineStorage` when an `evals` table is
|
|
6
|
+
* configured. Without those flags, eval keeps the in-process ephemeral path
|
|
7
|
+
* in `cli-ax.ts`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { EvalRunSnapshot } from "../evals.js";
|
|
11
|
+
import { matchesEvalIdFilter } from "../evals.js";
|
|
12
|
+
import type { EvalRunResult } from "./eval-runner.js";
|
|
13
|
+
import { mergeRequestHeaders, type RequestAuth } from "./request-headers.js";
|
|
14
|
+
|
|
15
|
+
const DEFAULT_POLL_MS = 1_000;
|
|
16
|
+
|
|
17
|
+
export type RemoteEvalErrorCode =
|
|
18
|
+
| "no_matching_evals"
|
|
19
|
+
| "eval_run_in_progress"
|
|
20
|
+
| "unknown_eval_run"
|
|
21
|
+
| "start_failed"
|
|
22
|
+
| "poll_failed"
|
|
23
|
+
| "aborted";
|
|
24
|
+
|
|
25
|
+
export class RemoteEvalError extends Error {
|
|
26
|
+
constructor(
|
|
27
|
+
message: string,
|
|
28
|
+
readonly status?: number,
|
|
29
|
+
readonly code?: RemoteEvalErrorCode,
|
|
30
|
+
readonly activeRunId?: string
|
|
31
|
+
) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "RemoteEvalError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RemoteEvalListItem {
|
|
38
|
+
id: string;
|
|
39
|
+
fileId: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
tags?: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RunRemoteEvalsOptions extends RequestAuth {
|
|
45
|
+
baseUrl: string;
|
|
46
|
+
filterIds?: string[];
|
|
47
|
+
tags?: string[];
|
|
48
|
+
/** Per-case timeout override forwarded to the serve process. */
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
/** Ask the serve harness to emit verbose case logs (server-side). */
|
|
51
|
+
verbose?: boolean;
|
|
52
|
+
fetchImpl?: typeof fetch;
|
|
53
|
+
/** Poll interval while the batch is `running`. */
|
|
54
|
+
pollMs?: number;
|
|
55
|
+
/** Abort the wait (not the remote batch). */
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
/** Called each time the polled snapshot changes. */
|
|
58
|
+
onSnapshot?: (snap: EvalRunSnapshot) => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Start a batch on the target serve via `POST /v1/dev/evals/runs`, poll until
|
|
63
|
+
* terminal, and return CLI-shaped case results.
|
|
64
|
+
*/
|
|
65
|
+
export async function runRemoteEvals(
|
|
66
|
+
options: RunRemoteEvalsOptions
|
|
67
|
+
): Promise<{ snapshot: EvalRunSnapshot; results: EvalRunResult[] }> {
|
|
68
|
+
const baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
69
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
70
|
+
const auth: RequestAuth = {
|
|
71
|
+
bearerToken: options.bearerToken,
|
|
72
|
+
headers: options.headers,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const started = await startRemoteEvalRun({
|
|
76
|
+
baseUrl,
|
|
77
|
+
auth,
|
|
78
|
+
fetchImpl,
|
|
79
|
+
filterIds: options.filterIds,
|
|
80
|
+
tags: options.tags,
|
|
81
|
+
timeoutMs: options.timeoutMs,
|
|
82
|
+
verbose: options.verbose,
|
|
83
|
+
signal: options.signal,
|
|
84
|
+
});
|
|
85
|
+
options.onSnapshot?.(started);
|
|
86
|
+
|
|
87
|
+
const snapshot = await pollRemoteEvalRun({
|
|
88
|
+
baseUrl,
|
|
89
|
+
runId: started.runId,
|
|
90
|
+
auth,
|
|
91
|
+
fetchImpl,
|
|
92
|
+
pollMs: options.pollMs ?? DEFAULT_POLL_MS,
|
|
93
|
+
signal: options.signal,
|
|
94
|
+
onSnapshot: options.onSnapshot,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return { snapshot, results: snapshotToEvalRunResults(snapshot) };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** List datapoints from `GET /v1/dev/evals` (same surface a remote run uses). */
|
|
101
|
+
export async function listRemoteEvals(args: {
|
|
102
|
+
baseUrl: string;
|
|
103
|
+
auth?: RequestAuth;
|
|
104
|
+
fetchImpl?: typeof fetch;
|
|
105
|
+
filterIds?: string[];
|
|
106
|
+
tags?: string[];
|
|
107
|
+
signal?: AbortSignal;
|
|
108
|
+
}): Promise<RemoteEvalListItem[]> {
|
|
109
|
+
const baseUrl = args.baseUrl.replace(/\/$/, "");
|
|
110
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
111
|
+
let resp: Response;
|
|
112
|
+
try {
|
|
113
|
+
resp = await fetchImpl(`${baseUrl}/v1/dev/evals`, {
|
|
114
|
+
headers: mergeRequestHeaders(args.auth),
|
|
115
|
+
signal: args.signal,
|
|
116
|
+
});
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw new RemoteEvalError(
|
|
119
|
+
error instanceof Error ? error.message : "Could not list evals.",
|
|
120
|
+
undefined,
|
|
121
|
+
"start_failed"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (!resp.ok) {
|
|
125
|
+
throw new RemoteEvalError(
|
|
126
|
+
`Could not list evals (HTTP ${resp.status}).`,
|
|
127
|
+
resp.status,
|
|
128
|
+
"start_failed"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const body = (await resp.json()) as {
|
|
132
|
+
evals?: Array<{
|
|
133
|
+
id?: unknown;
|
|
134
|
+
fileId?: unknown;
|
|
135
|
+
description?: unknown;
|
|
136
|
+
tags?: unknown;
|
|
137
|
+
}>;
|
|
138
|
+
};
|
|
139
|
+
const listed: RemoteEvalListItem[] = [];
|
|
140
|
+
for (const row of body.evals ?? []) {
|
|
141
|
+
if (typeof row.id !== "string" || row.id === "") {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (typeof row.fileId !== "string" || row.fileId === "") {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const tags = Array.isArray(row.tags)
|
|
148
|
+
? row.tags.filter((t): t is string => typeof t === "string")
|
|
149
|
+
: undefined;
|
|
150
|
+
listed.push({
|
|
151
|
+
id: row.id,
|
|
152
|
+
fileId: row.fileId,
|
|
153
|
+
...(typeof row.description === "string"
|
|
154
|
+
? { description: row.description }
|
|
155
|
+
: {}),
|
|
156
|
+
...(tags === undefined || tags.length === 0 ? {} : { tags }),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let selected = listed;
|
|
161
|
+
if (args.filterIds !== undefined && args.filterIds.length > 0) {
|
|
162
|
+
selected = selected.filter((e) =>
|
|
163
|
+
args.filterIds!.some((f) => matchesEvalIdFilter(e.id, f))
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (args.tags !== undefined && args.tags.length > 0) {
|
|
167
|
+
selected = selected.filter((e) =>
|
|
168
|
+
(e.tags ?? []).some((t) => args.tags!.includes(t))
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return selected;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function startRemoteEvalRun(args: {
|
|
175
|
+
baseUrl: string;
|
|
176
|
+
auth?: RequestAuth;
|
|
177
|
+
fetchImpl?: typeof fetch;
|
|
178
|
+
filterIds?: string[];
|
|
179
|
+
tags?: string[];
|
|
180
|
+
timeoutMs?: number;
|
|
181
|
+
verbose?: boolean;
|
|
182
|
+
signal?: AbortSignal;
|
|
183
|
+
}): Promise<EvalRunSnapshot> {
|
|
184
|
+
const baseUrl = args.baseUrl.replace(/\/$/, "");
|
|
185
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
186
|
+
const body: {
|
|
187
|
+
filterIds?: string[];
|
|
188
|
+
tags?: string[];
|
|
189
|
+
timeoutMs?: number;
|
|
190
|
+
verbose?: boolean;
|
|
191
|
+
} = {};
|
|
192
|
+
if (args.filterIds !== undefined && args.filterIds.length > 0) {
|
|
193
|
+
body.filterIds = args.filterIds;
|
|
194
|
+
}
|
|
195
|
+
if (args.tags !== undefined && args.tags.length > 0) {
|
|
196
|
+
body.tags = args.tags;
|
|
197
|
+
}
|
|
198
|
+
if (args.timeoutMs !== undefined) {
|
|
199
|
+
body.timeoutMs = args.timeoutMs;
|
|
200
|
+
}
|
|
201
|
+
if (args.verbose === true) {
|
|
202
|
+
body.verbose = true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let resp: Response;
|
|
206
|
+
try {
|
|
207
|
+
resp = await fetchImpl(`${baseUrl}/v1/dev/evals/runs`, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers: {
|
|
210
|
+
"content-type": "application/json",
|
|
211
|
+
...mergeRequestHeaders(args.auth),
|
|
212
|
+
},
|
|
213
|
+
body: JSON.stringify(body),
|
|
214
|
+
signal: args.signal,
|
|
215
|
+
});
|
|
216
|
+
} catch (error) {
|
|
217
|
+
throw new RemoteEvalError(
|
|
218
|
+
error instanceof Error ? error.message : "Could not start eval run.",
|
|
219
|
+
undefined,
|
|
220
|
+
"start_failed"
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const json = (await resp.json().catch(() => ({}))) as {
|
|
225
|
+
ok?: boolean;
|
|
226
|
+
error?: string;
|
|
227
|
+
message?: string;
|
|
228
|
+
activeRunId?: string;
|
|
229
|
+
runId?: string;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
if (json.error === "no_matching_evals") {
|
|
233
|
+
throw new RemoteEvalError(
|
|
234
|
+
"No matching evals found.",
|
|
235
|
+
404,
|
|
236
|
+
"no_matching_evals"
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
if (resp.status === 409 || json.error === "eval_run_in_progress") {
|
|
240
|
+
throw new RemoteEvalError(
|
|
241
|
+
json.activeRunId !== undefined && json.activeRunId !== ""
|
|
242
|
+
? `An eval run is already in progress (${json.activeRunId}).`
|
|
243
|
+
: "An eval run is already in progress.",
|
|
244
|
+
409,
|
|
245
|
+
"eval_run_in_progress",
|
|
246
|
+
typeof json.activeRunId === "string" ? json.activeRunId : undefined
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (!resp.ok) {
|
|
250
|
+
const detail =
|
|
251
|
+
typeof json.message === "string" && json.message !== ""
|
|
252
|
+
? json.message
|
|
253
|
+
: typeof json.error === "string" && json.error !== ""
|
|
254
|
+
? json.error
|
|
255
|
+
: `HTTP ${resp.status}`;
|
|
256
|
+
throw new RemoteEvalError(
|
|
257
|
+
`Could not start eval run: ${detail}`,
|
|
258
|
+
resp.status,
|
|
259
|
+
"start_failed"
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
if (typeof json.runId !== "string" || json.runId === "") {
|
|
263
|
+
throw new RemoteEvalError(
|
|
264
|
+
"Eval start response missing runId.",
|
|
265
|
+
undefined,
|
|
266
|
+
"start_failed"
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return json as EvalRunSnapshot;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** One-shot `GET /v1/dev/evals/runs/:runId` (no polling). */
|
|
273
|
+
export async function getRemoteEvalRun(args: {
|
|
274
|
+
baseUrl: string;
|
|
275
|
+
runId: string;
|
|
276
|
+
auth?: RequestAuth;
|
|
277
|
+
fetchImpl?: typeof fetch;
|
|
278
|
+
signal?: AbortSignal;
|
|
279
|
+
}): Promise<EvalRunSnapshot> {
|
|
280
|
+
const baseUrl = args.baseUrl.replace(/\/$/, "");
|
|
281
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
282
|
+
let resp: Response;
|
|
283
|
+
try {
|
|
284
|
+
resp = await fetchImpl(
|
|
285
|
+
`${baseUrl}/v1/dev/evals/runs/${encodeURIComponent(args.runId)}`,
|
|
286
|
+
{
|
|
287
|
+
headers: mergeRequestHeaders(args.auth),
|
|
288
|
+
signal: args.signal,
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
throw new RemoteEvalError(
|
|
293
|
+
error instanceof Error ? error.message : "Could not fetch eval run.",
|
|
294
|
+
undefined,
|
|
295
|
+
"poll_failed"
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (resp.status === 404) {
|
|
300
|
+
throw new RemoteEvalError(
|
|
301
|
+
`Unknown eval run ${args.runId}.`,
|
|
302
|
+
404,
|
|
303
|
+
"unknown_eval_run"
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
if (!resp.ok) {
|
|
307
|
+
throw new RemoteEvalError(
|
|
308
|
+
`Could not fetch eval run (HTTP ${resp.status}).`,
|
|
309
|
+
resp.status,
|
|
310
|
+
"poll_failed"
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return (await resp.json()) as EvalRunSnapshot;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Recent batches from `GET /v1/dev/evals/runs`. */
|
|
317
|
+
export async function listRemoteEvalRuns(args: {
|
|
318
|
+
baseUrl: string;
|
|
319
|
+
auth?: RequestAuth;
|
|
320
|
+
fetchImpl?: typeof fetch;
|
|
321
|
+
signal?: AbortSignal;
|
|
322
|
+
}): Promise<{ runs: EvalRunSnapshot[]; activeRunId?: string }> {
|
|
323
|
+
const baseUrl = args.baseUrl.replace(/\/$/, "");
|
|
324
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
325
|
+
let resp: Response;
|
|
326
|
+
try {
|
|
327
|
+
resp = await fetchImpl(`${baseUrl}/v1/dev/evals/runs`, {
|
|
328
|
+
headers: mergeRequestHeaders(args.auth),
|
|
329
|
+
signal: args.signal,
|
|
330
|
+
});
|
|
331
|
+
} catch (error) {
|
|
332
|
+
throw new RemoteEvalError(
|
|
333
|
+
error instanceof Error ? error.message : "Could not list eval runs.",
|
|
334
|
+
undefined,
|
|
335
|
+
"poll_failed"
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
if (!resp.ok) {
|
|
339
|
+
throw new RemoteEvalError(
|
|
340
|
+
`Could not list eval runs (HTTP ${resp.status}).`,
|
|
341
|
+
resp.status,
|
|
342
|
+
"poll_failed"
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const body = (await resp.json()) as {
|
|
346
|
+
runs?: unknown;
|
|
347
|
+
activeRunId?: unknown;
|
|
348
|
+
};
|
|
349
|
+
const runs = Array.isArray(body.runs)
|
|
350
|
+
? (body.runs.filter(
|
|
351
|
+
(r): r is EvalRunSnapshot =>
|
|
352
|
+
r !== null &&
|
|
353
|
+
typeof r === "object" &&
|
|
354
|
+
typeof (r as EvalRunSnapshot).runId === "string"
|
|
355
|
+
) as EvalRunSnapshot[])
|
|
356
|
+
: [];
|
|
357
|
+
return {
|
|
358
|
+
runs,
|
|
359
|
+
...(typeof body.activeRunId === "string" && body.activeRunId !== ""
|
|
360
|
+
? { activeRunId: body.activeRunId }
|
|
361
|
+
: {}),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export async function pollRemoteEvalRun(args: {
|
|
366
|
+
baseUrl: string;
|
|
367
|
+
runId: string;
|
|
368
|
+
auth?: RequestAuth;
|
|
369
|
+
fetchImpl?: typeof fetch;
|
|
370
|
+
pollMs?: number;
|
|
371
|
+
signal?: AbortSignal;
|
|
372
|
+
onSnapshot?: (snap: EvalRunSnapshot) => void;
|
|
373
|
+
}): Promise<EvalRunSnapshot> {
|
|
374
|
+
const pollMs = args.pollMs ?? DEFAULT_POLL_MS;
|
|
375
|
+
let lastJson = "";
|
|
376
|
+
|
|
377
|
+
for (;;) {
|
|
378
|
+
if (args.signal?.aborted === true) {
|
|
379
|
+
throw new RemoteEvalError("Eval wait aborted.", undefined, "aborted");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const snap = await getRemoteEvalRun(args);
|
|
383
|
+
const serialized = JSON.stringify(snap);
|
|
384
|
+
if (serialized !== lastJson) {
|
|
385
|
+
lastJson = serialized;
|
|
386
|
+
args.onSnapshot?.(snap);
|
|
387
|
+
}
|
|
388
|
+
if (snap.status !== "running") {
|
|
389
|
+
return snap;
|
|
390
|
+
}
|
|
391
|
+
await sleep(pollMs, args.signal);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export function snapshotToEvalRunResults(
|
|
396
|
+
snapshot: EvalRunSnapshot
|
|
397
|
+
): EvalRunResult[] {
|
|
398
|
+
return snapshot.cases.map((c) => ({
|
|
399
|
+
id: c.id,
|
|
400
|
+
path: c.fileId,
|
|
401
|
+
...(c.description === undefined ? {} : { description: c.description }),
|
|
402
|
+
ok: c.ok === true,
|
|
403
|
+
assertions: c.assertions ?? [],
|
|
404
|
+
...(c.sessionId === undefined ? {} : { sessionId: c.sessionId }),
|
|
405
|
+
inputs: c.inputs ?? [],
|
|
406
|
+
...(c.finalText === undefined ? {} : { finalText: c.finalText }),
|
|
407
|
+
...(c.tools === undefined ? {} : { tools: c.tools }),
|
|
408
|
+
...(c.toolCalls === undefined ? {} : { toolCalls: c.toolCalls }),
|
|
409
|
+
...(c.error === undefined ? {} : { error: c.error }),
|
|
410
|
+
logs: c.logs ?? [],
|
|
411
|
+
durationMs: c.durationMs ?? 0,
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
416
|
+
return new Promise((resolve, reject) => {
|
|
417
|
+
if (signal?.aborted === true) {
|
|
418
|
+
reject(new RemoteEvalError("Eval wait aborted.", undefined, "aborted"));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const timer = setTimeout(() => {
|
|
422
|
+
signal?.removeEventListener("abort", onAbort);
|
|
423
|
+
resolve();
|
|
424
|
+
}, ms);
|
|
425
|
+
const onAbort = (): void => {
|
|
426
|
+
clearTimeout(timer);
|
|
427
|
+
reject(new RemoteEvalError("Eval wait aborted.", undefined, "aborted"));
|
|
428
|
+
};
|
|
429
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
430
|
+
});
|
|
431
|
+
}
|
|
@@ -433,15 +433,15 @@ Recommended loop once the project exists:
|
|
|
433
433
|
1. Edit agent/ + tools under agent/tools/
|
|
434
434
|
2. ${CLI_COMMAND_NAME} info --dir . --json
|
|
435
435
|
3. ${CLI_COMMAND_NAME} run --dir . --message "..."
|
|
436
|
-
4. ${CLI_COMMAND_NAME} eval --
|
|
436
|
+
4. ${CLI_COMMAND_NAME} eval --dir .
|
|
437
437
|
|
|
438
438
|
Evals:
|
|
439
439
|
Create evals/**/*.eval.ts using defineEval from ${PACKAGE_NAME}/evals
|
|
440
440
|
Case id is the path: evals/weather/nyc.eval.ts → weather/nyc
|
|
441
441
|
Required: evals/evals.config.ts with defineEvalConfig({ maxConcurrency })
|
|
442
442
|
(hard-capped at 200 due to model provider request limits)
|
|
443
|
-
Run: ${CLI_COMMAND_NAME} eval
|
|
444
|
-
(
|
|
443
|
+
Run: ${CLI_COMMAND_NAME} eval [--json] [evalId...]
|
|
444
|
+
(pass --prod to run against the hosted deployment)
|
|
445
445
|
|
|
446
446
|
See AGENTS.md in the ${PACKAGE_NAME} package for the full loop.
|
|
447
447
|
`);
|
package/src/internal/server.ts
CHANGED
|
@@ -1082,6 +1082,8 @@ function mountAgentRoutes(
|
|
|
1082
1082
|
const body = await readJsonObject(request);
|
|
1083
1083
|
const filterIds = asStringArray(body?.filterIds);
|
|
1084
1084
|
const tags = asStringArray(body?.tags);
|
|
1085
|
+
const timeoutMs = asPositiveInt(body?.timeoutMs);
|
|
1086
|
+
const verbose = body?.verbose === true;
|
|
1085
1087
|
const authHeader = request.headers.get("authorization");
|
|
1086
1088
|
const bearerToken = authHeader?.toLowerCase().startsWith("bearer ")
|
|
1087
1089
|
? authHeader.slice("bearer ".length).trim()
|
|
@@ -1093,6 +1095,8 @@ function mountAgentRoutes(
|
|
|
1093
1095
|
...(bearerToken === undefined || bearerToken === ""
|
|
1094
1096
|
? {}
|
|
1095
1097
|
: { bearerToken }),
|
|
1098
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
1099
|
+
...(verbose ? { verbose: true } : {}),
|
|
1096
1100
|
});
|
|
1097
1101
|
return jsonResponse(snap, { status: 202 });
|
|
1098
1102
|
} catch (error) {
|
|
@@ -1896,6 +1900,14 @@ function asStringArray(value: unknown): string[] | undefined {
|
|
|
1896
1900
|
return out.length === 0 ? undefined : out;
|
|
1897
1901
|
}
|
|
1898
1902
|
|
|
1903
|
+
function asPositiveInt(value: unknown): number | undefined {
|
|
1904
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1905
|
+
return undefined;
|
|
1906
|
+
}
|
|
1907
|
+
const n = Math.floor(value);
|
|
1908
|
+
return n > 0 ? n : undefined;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1899
1911
|
async function assertReminderReadable(input: {
|
|
1900
1912
|
engine: SessionEngine;
|
|
1901
1913
|
reminder: ReminderInfo;
|