@cursor/july 0.1.4 → 0.1.6
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/ab.d.ts +8 -95
- package/dist/ab.d.ts.map +1 -1
- package/dist/ab.js +9 -150
- package/dist/bin/agent-serve.js +14 -6
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DF-Yx4OP.js → app.DMboGKUD.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.O6eP7QZr.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DSeJSudL.js → VPLocalSearchBox.DkEqEnJU.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DZVLTXOC.js → theme.ASugdMNm.js} +2 -2
- package/dist/docs/assets/deployment.md.BtfEsc9S.js +55 -0
- package/dist/docs/assets/deployment.md.BtfEsc9S.lean.js +1 -0
- package/dist/docs/assets/example-agents_approval-buddy.md.8R5phXb5.js +10 -0
- package/dist/docs/assets/example-agents_approval-buddy.md.8R5phXb5.lean.js +1 -0
- package/dist/docs/assets/example-agents_benny.md.B0gjhI-p.js +7 -0
- package/dist/docs/assets/example-agents_benny.md.B0gjhI-p.lean.js +1 -0
- package/dist/docs/assets/example-agents_bugbot.md.DelIdhxB.js +11 -0
- package/dist/docs/assets/example-agents_bugbot.md.DelIdhxB.lean.js +1 -0
- package/dist/docs/assets/example-agents_codebase-wiki.md.DC6sgwn0.js +8 -0
- package/dist/docs/assets/example-agents_codebase-wiki.md.DC6sgwn0.lean.js +1 -0
- package/dist/docs/assets/example-agents_codeowners-review.md.Ku_tG2RY.js +8 -0
- package/dist/docs/assets/example-agents_codeowners-review.md.Ku_tG2RY.lean.js +1 -0
- package/dist/docs/assets/example-agents_concierge.md.4rQTSMXt.js +23 -0
- package/dist/docs/assets/example-agents_concierge.md.4rQTSMXt.lean.js +1 -0
- package/dist/docs/assets/example-agents_fsd.md.CzgUrDfi.js +15 -0
- package/dist/docs/assets/example-agents_fsd.md.CzgUrDfi.lean.js +1 -0
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +2 -0
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.lean.js +1 -0
- package/dist/docs/assets/example-agents_knowledge-base.md.BPJiVueF.js +11 -0
- package/dist/docs/assets/example-agents_knowledge-base.md.BPJiVueF.lean.js +1 -0
- package/dist/docs/assets/example-agents_security-reviewer.md.D2rtwDTO.js +19 -0
- package/dist/docs/assets/example-agents_security-reviewer.md.D2rtwDTO.lean.js +1 -0
- package/dist/docs/assets/example-agents_slack-agent.md.buLbgvBf.js +5 -0
- package/dist/docs/assets/example-agents_slack-agent.md.buLbgvBf.lean.js +1 -0
- package/dist/docs/assets/example-agents_weather-agent.md.C9Qv-W0o.js +24 -0
- package/dist/docs/assets/example-agents_weather-agent.md.C9Qv-W0o.lean.js +1 -0
- package/dist/docs/assets/index.md.COiu-1jL.js +20 -0
- package/dist/docs/assets/{index.md.Cylk70gg.lean.js → index.md.COiu-1jL.lean.js} +1 -1
- package/dist/docs/assets/reference_cli.md.D189RBCH.js +60 -0
- package/dist/docs/assets/reference_cli.md.D189RBCH.lean.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +58 -17
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +34 -0
- package/dist/docs/example-agents/benny.html +31 -0
- package/dist/docs/example-agents/bugbot.html +35 -0
- package/dist/docs/example-agents/codebase-wiki.html +32 -0
- package/dist/docs/example-agents/codeowners-review.html +32 -0
- package/dist/docs/example-agents/concierge.html +47 -0
- package/dist/docs/example-agents/fsd.html +39 -0
- package/dist/docs/example-agents/index.html +26 -0
- package/dist/docs/example-agents/knowledge-base.html +35 -0
- package/dist/docs/example-agents/security-reviewer.html +43 -0
- package/dist/docs/example-agents/slack-agent.html +29 -0
- package/dist/docs/example-agents/weather-agent.html +48 -0
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +5 -5
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +7 -7
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +52 -30
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/evals.d.ts +4 -61
- package/dist/evals.d.ts.map +1 -1
- package/dist/evals.js +3 -66
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/ab-collector.d.ts +7 -5
- package/dist/internal/ab-collector.d.ts.map +1 -1
- package/dist/internal/ab-collector.js +3 -14
- package/dist/internal/ab-snapshot.d.ts +2 -4
- package/dist/internal/ab-snapshot.d.ts.map +1 -1
- package/dist/internal/cli-ax.d.ts +18 -3
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +80 -42
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +5 -2
- package/dist/internal/cursor-event-relay.d.ts +4 -1
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +6 -0
- package/dist/internal/cursor-relay-core.d.ts +36 -1
- package/dist/internal/cursor-relay-core.d.ts.map +1 -1
- package/dist/internal/cursor-relay-core.js +89 -8
- package/dist/internal/cursor-slack-relay.d.ts +2 -2
- package/dist/internal/cursor-slack-relay.d.ts.map +1 -1
- package/dist/internal/cursor-slack-relay.js +4 -0
- package/dist/internal/discovery.js +3 -3
- package/dist/internal/eval-run-store.d.ts +33 -30
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +84 -96
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +3 -2
- package/dist/internal/playground-proxy.d.ts +5 -5
- package/dist/internal/playground-proxy.js +3 -3
- package/dist/internal/resolve-prod-target.d.ts +30 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -1
- package/dist/internal/resolve-prod-target.js +74 -2
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +5 -3
- package/dist/internal/session-engine.d.ts +1 -2
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +14 -31
- package/dist/internal/storage-coordinator.d.ts +16 -15
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +73 -80
- package/dist/playground/assets/cursor-icons-16-CQ50JpfO.woff2 +0 -0
- package/dist/playground/assets/index-7GJIUFQj.css +1 -0
- package/dist/playground/assets/index-mux9aj5P.js +319 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage.d.ts +51 -10
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +27 -10
- package/docs/README.md +34 -5
- package/docs/deployment.md +352 -149
- package/docs/example-agents/approval-buddy.md +270 -0
- package/docs/example-agents/benny.md +186 -0
- package/docs/example-agents/bugbot.md +231 -0
- package/docs/example-agents/codebase-wiki.md +174 -0
- package/docs/example-agents/codeowners-review.md +195 -0
- package/docs/example-agents/concierge.md +205 -0
- package/docs/example-agents/fsd.md +330 -0
- package/docs/example-agents/index.md +102 -0
- package/docs/example-agents/knowledge-base.md +171 -0
- package/docs/example-agents/security-reviewer.md +296 -0
- package/docs/example-agents/slack-agent.md +146 -0
- package/docs/example-agents/weather-agent.md +302 -0
- package/docs/reference/cli.md +546 -147
- package/package.json +1 -1
- package/src/ab.ts +9 -261
- package/src/bin/agent-serve.ts +14 -6
- package/src/evals.ts +4 -118
- package/src/index.ts +2 -0
- package/src/internal/ab-collector.ts +12 -22
- package/src/internal/ab-snapshot.ts +2 -4
- package/src/internal/cli-ax.ts +101 -45
- package/src/internal/cli-deploy.ts +5 -2
- package/src/internal/cursor-event-relay.ts +10 -1
- package/src/internal/cursor-relay-core.ts +115 -9
- package/src/internal/cursor-slack-relay.ts +6 -2
- package/src/internal/discovery.ts +2 -2
- package/src/internal/eval-run-store.ts +86 -100
- package/src/internal/init-project.ts +3 -2
- package/src/internal/playground-proxy.ts +5 -5
- package/src/internal/resolve-prod-target.ts +101 -3
- package/src/internal/server.ts +5 -3
- package/src/internal/session-engine.ts +9 -29
- package/src/internal/storage-coordinator.ts +109 -101
- package/src/storage.ts +79 -14
- package/dist/docs/assets/chunks/@localSearchIndexroot.0XrheBv2.js +0 -1
- package/dist/docs/assets/deployment.md.DTKwE15Z.js +0 -14
- package/dist/docs/assets/deployment.md.DTKwE15Z.lean.js +0 -1
- package/dist/docs/assets/index.md.Cylk70gg.js +0 -20
- package/dist/docs/assets/reference_cli.md.Bv6pOxcF.js +0 -38
- package/dist/docs/assets/reference_cli.md.Bv6pOxcF.lean.js +0 -1
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/json-dir-store.js +0 -100
- package/dist/playground/assets/cursor-icons-outline-BxTT_FVJ.woff2 +0 -0
- package/dist/playground/assets/index-Ct_Usdm8.css +0 -1
- package/dist/playground/assets/index-Dsyb-Vaj.js +0 -319
- package/src/internal/json-dir-store.ts +0 -109
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Store for playground-triggered eval batches.
|
|
3
3
|
*
|
|
4
|
-
* Default: process memory only, capped by `maxPlaygroundRuns` (see
|
|
5
|
-
*
|
|
4
|
+
* Default: process memory only, capped by `maxPlaygroundRuns` (see
|
|
5
|
+
* `defineEvalConfig`). When `agent/storage.ts` (`defineStorage`) declares an
|
|
6
|
+
* `evals` table, runs are mirrored there and survive restart.
|
|
6
7
|
* Runs against the live mount's loopback URL so case sessions appear in
|
|
7
8
|
* the playground session list.
|
|
8
9
|
*/
|
|
@@ -11,7 +12,6 @@ import { randomUUID } from "node:crypto";
|
|
|
11
12
|
import {
|
|
12
13
|
type EvalCaseSnapshot,
|
|
13
14
|
type EvalConfigFile,
|
|
14
|
-
type EvalRunPersistence,
|
|
15
15
|
type EvalRunSnapshot,
|
|
16
16
|
resolveEvalMaxPlaygroundRuns,
|
|
17
17
|
} from "../evals.js";
|
|
@@ -30,6 +30,18 @@ export type {
|
|
|
30
30
|
EvalRunSnapshot,
|
|
31
31
|
} from "../evals.js";
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Durable mirror for playground eval batches — the `StorageCoordinator`
|
|
35
|
+
* adapter over the `defineStorage` `evals` table. Writes are
|
|
36
|
+
* fire-and-forget (the coordinator serializes and isolates failures);
|
|
37
|
+
* `list` is the serve-start hydrate read.
|
|
38
|
+
*/
|
|
39
|
+
export interface EvalRunStorage {
|
|
40
|
+
save(run: EvalRunSnapshot): void;
|
|
41
|
+
delete(runId: string): void;
|
|
42
|
+
list(): Promise<EvalRunSnapshot[]>;
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
export interface StartEvalRunInput {
|
|
34
46
|
filterIds?: string[];
|
|
35
47
|
tags?: string[];
|
|
@@ -41,7 +53,7 @@ export type EvalListConfig = {
|
|
|
41
53
|
maxConcurrency?: number;
|
|
42
54
|
timeoutMs?: number;
|
|
43
55
|
maxPlaygroundRuns: number;
|
|
44
|
-
/** True when `
|
|
56
|
+
/** True when `agent/storage.ts` declares an `evals` table. */
|
|
45
57
|
durableRuns: boolean;
|
|
46
58
|
};
|
|
47
59
|
|
|
@@ -55,28 +67,16 @@ export class EvalRunStore {
|
|
|
55
67
|
private activeRunId: string | undefined;
|
|
56
68
|
private hydratePromise: Promise<void> | undefined;
|
|
57
69
|
private hydrated = false;
|
|
58
|
-
/** Per-run persist chain so intermediate saves cannot overwrite a later final write. */
|
|
59
|
-
private readonly persistChains = new Map<string, Promise<void>>();
|
|
60
70
|
/** Filled after listen — e.g. `http://127.0.0.1:3000/weather-agent`. */
|
|
61
71
|
loopbackUrl: string | undefined;
|
|
62
72
|
|
|
63
73
|
constructor(
|
|
64
74
|
private readonly projectRoot: string,
|
|
65
75
|
private readonly logger: (line: string) => void,
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
* used when `evals.config.ts` sets no `persistRuns` of its own.
|
|
69
|
-
*/
|
|
70
|
-
private readonly fallbackPersistence?: EvalRunPersistence
|
|
76
|
+
/** `defineStorage` evals-table adapter; undefined ⇒ memory only. */
|
|
77
|
+
private readonly storage?: EvalRunStorage | undefined
|
|
71
78
|
) {}
|
|
72
79
|
|
|
73
|
-
/** `evals.config.ts` `persistRuns` wins; `defineStorage` is fallback. */
|
|
74
|
-
private resolvePersist(
|
|
75
|
-
config: Partial<EvalConfigFile>
|
|
76
|
-
): EvalRunPersistence | undefined {
|
|
77
|
-
return config.persistRuns ?? this.fallbackPersistence;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
80
|
async list(): Promise<{
|
|
81
81
|
evals: Array<{
|
|
82
82
|
id: string;
|
|
@@ -97,10 +97,7 @@ export class EvalRunStore {
|
|
|
97
97
|
: { description: e.definition.description }),
|
|
98
98
|
...(e.definition.tags === undefined ? {} : { tags: e.definition.tags }),
|
|
99
99
|
})),
|
|
100
|
-
config: snapshotListConfig(
|
|
101
|
-
config,
|
|
102
|
-
this.resolvePersist(config) !== undefined
|
|
103
|
-
),
|
|
100
|
+
config: snapshotListConfig(config, this.storage !== undefined),
|
|
104
101
|
};
|
|
105
102
|
}
|
|
106
103
|
|
|
@@ -175,15 +172,12 @@ export class EvalRunStore {
|
|
|
175
172
|
done: 0,
|
|
176
173
|
},
|
|
177
174
|
cases,
|
|
178
|
-
config: snapshotRunConfig(
|
|
179
|
-
config,
|
|
180
|
-
this.resolvePersist(config) !== undefined
|
|
181
|
-
),
|
|
175
|
+
config: snapshotRunConfig(config, this.storage !== undefined),
|
|
182
176
|
};
|
|
183
177
|
this.runs.set(runId, snapshot);
|
|
184
178
|
this.activeRunId = runId;
|
|
185
|
-
|
|
186
|
-
|
|
179
|
+
this.pruneOldRuns(config);
|
|
180
|
+
this.persist(snapshot);
|
|
187
181
|
this.logger(
|
|
188
182
|
`[agentkit] eval run ${runId}: starting ${cases.length} case(s)`
|
|
189
183
|
);
|
|
@@ -208,39 +202,28 @@ export class EvalRunStore {
|
|
|
208
202
|
}
|
|
209
203
|
|
|
210
204
|
/**
|
|
211
|
-
* Hydrate playground eval history from
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* Idempotent; concurrent callers share one pass.
|
|
205
|
+
* Hydrate playground eval history from the `defineStorage` evals table.
|
|
206
|
+
* Called eagerly from the serve startup restore step — alongside session
|
|
207
|
+
* restore — and lazily as a safety net by every read. Idempotent;
|
|
208
|
+
* concurrent callers share one pass.
|
|
216
209
|
*/
|
|
217
210
|
async hydrate(): Promise<void> {
|
|
218
211
|
if (this.hydrated) {
|
|
219
212
|
return;
|
|
220
213
|
}
|
|
221
214
|
if (this.hydratePromise === undefined) {
|
|
222
|
-
this.hydratePromise = this.
|
|
215
|
+
this.hydratePromise = this.hydrateFromStorage();
|
|
223
216
|
}
|
|
224
217
|
await this.hydratePromise;
|
|
225
218
|
}
|
|
226
219
|
|
|
227
|
-
private async
|
|
220
|
+
private async hydrateFromStorage(): Promise<void> {
|
|
228
221
|
try {
|
|
229
|
-
|
|
230
|
-
const persist = this.resolvePersist(config);
|
|
231
|
-
if (persist === undefined) {
|
|
232
|
-
this.hydrated = true;
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
const loaded = await persist.load({ projectRoot: this.projectRoot });
|
|
236
|
-
if (!Array.isArray(loaded)) {
|
|
237
|
-
this.logger(
|
|
238
|
-
"[agentkit] eval persistRuns.load() did not return an array; ignoring"
|
|
239
|
-
);
|
|
222
|
+
if (this.storage === undefined) {
|
|
240
223
|
this.hydrated = true;
|
|
241
224
|
return;
|
|
242
225
|
}
|
|
243
|
-
const
|
|
226
|
+
const loaded = await this.storage.list();
|
|
244
227
|
for (const run of loaded) {
|
|
245
228
|
if (
|
|
246
229
|
run === null ||
|
|
@@ -253,16 +236,26 @@ export class EvalRunStore {
|
|
|
253
236
|
// Incomplete batches from a killed process should not block a new run.
|
|
254
237
|
if (run.status === "running") {
|
|
255
238
|
markInterruptedRun(run);
|
|
256
|
-
|
|
239
|
+
this.persist(run);
|
|
257
240
|
}
|
|
258
241
|
this.runs.set(run.runId, run);
|
|
259
242
|
}
|
|
260
|
-
|
|
261
|
-
|
|
243
|
+
// Prune with the defaults even when eval discovery fails — loaded
|
|
244
|
+
// history must not stay unpruned for the process lifetime just
|
|
245
|
+
// because an eval file has a syntax error at boot.
|
|
246
|
+
let config: Partial<EvalConfigFile> = {};
|
|
247
|
+
try {
|
|
248
|
+
config = (await discoverEvals(this.projectRoot)).config;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
this.logger(
|
|
251
|
+
`[agentkit] eval discovery failed during hydrate (pruning with defaults): ${
|
|
252
|
+
error instanceof Error ? error.message : String(error)
|
|
253
|
+
}`
|
|
254
|
+
);
|
|
262
255
|
}
|
|
263
|
-
|
|
256
|
+
this.pruneOldRuns(config);
|
|
264
257
|
this.logger(
|
|
265
|
-
`[agentkit] hydrated ${this.runs.size} playground eval run(s) from
|
|
258
|
+
`[agentkit] hydrated ${this.runs.size} playground eval run(s) from storage`
|
|
266
259
|
);
|
|
267
260
|
} catch (error) {
|
|
268
261
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -274,7 +267,7 @@ export class EvalRunStore {
|
|
|
274
267
|
}
|
|
275
268
|
}
|
|
276
269
|
|
|
277
|
-
private
|
|
270
|
+
private pruneOldRuns(config: Partial<EvalConfigFile>): void {
|
|
278
271
|
const limit = resolveEvalMaxPlaygroundRuns(config.maxPlaygroundRuns);
|
|
279
272
|
const ordered = this.sortedRuns();
|
|
280
273
|
if (ordered.length <= limit) {
|
|
@@ -285,58 +278,34 @@ export class EvalRunStore {
|
|
|
285
278
|
continue;
|
|
286
279
|
}
|
|
287
280
|
this.runs.delete(stale.runId);
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Queue a durable save for `run`. Snapshots are cloned at enqueue time and
|
|
294
|
-
* writes for the same runId run strictly in order so an intermediate
|
|
295
|
-
* fire-and-forget save cannot finish after (and overwrite) a later final save.
|
|
296
|
-
*/
|
|
297
|
-
private enqueuePersist(
|
|
298
|
-
run: EvalRunSnapshot,
|
|
299
|
-
persist: EvalRunPersistence | undefined
|
|
300
|
-
): Promise<void> {
|
|
301
|
-
if (persist === undefined) {
|
|
302
|
-
return Promise.resolve();
|
|
281
|
+
this.storage?.delete(stale.runId);
|
|
303
282
|
}
|
|
304
|
-
const snapshot = structuredClone(run);
|
|
305
|
-
const prev = this.persistChains.get(run.runId) ?? Promise.resolve();
|
|
306
|
-
const next = prev
|
|
307
|
-
.catch(() => undefined)
|
|
308
|
-
.then(() => this.persistSave(snapshot, persist));
|
|
309
|
-
this.persistChains.set(run.runId, next);
|
|
310
|
-
return next;
|
|
311
283
|
}
|
|
312
284
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
): Promise<void> {
|
|
317
|
-
try {
|
|
318
|
-
await persist.save(run, { projectRoot: this.projectRoot });
|
|
319
|
-
} catch (error) {
|
|
320
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
321
|
-
this.logger(
|
|
322
|
-
`[agentkit] persistRuns.save(${run.runId}) failed: ${message}`
|
|
323
|
-
);
|
|
324
|
-
}
|
|
285
|
+
/** Mirror the run onto durable storage (coordinator clones + serializes). */
|
|
286
|
+
private persist(run: EvalRunSnapshot): void {
|
|
287
|
+
this.storage?.save(run);
|
|
325
288
|
}
|
|
326
289
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
290
|
+
/**
|
|
291
|
+
* Serve is draining: durably mark any in-flight batch as interrupted
|
|
292
|
+
* while the storage queue can still deliver. Late `execute` writes are
|
|
293
|
+
* dropped by the closed coordinator, so without this a batch that was
|
|
294
|
+
* running at shutdown would look "running" (or silently lose its final
|
|
295
|
+
* completion write) until the next hydrate repairs it.
|
|
296
|
+
*/
|
|
297
|
+
close(): void {
|
|
298
|
+
const activeRunId = this.activeRunId;
|
|
299
|
+
if (activeRunId === undefined) {
|
|
332
300
|
return;
|
|
333
301
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
338
|
-
this.logger(`[agentkit] persistRuns.delete(${runId}) failed: ${message}`);
|
|
302
|
+
const run = this.runs.get(activeRunId);
|
|
303
|
+
if (run === undefined || run.status !== "running") {
|
|
304
|
+
return;
|
|
339
305
|
}
|
|
306
|
+
markInterruptedRun(run);
|
|
307
|
+
this.persist(run);
|
|
308
|
+
this.activeRunId = undefined;
|
|
340
309
|
}
|
|
341
310
|
|
|
342
311
|
private async execute(
|
|
@@ -408,7 +377,7 @@ export class EvalRunStore {
|
|
|
408
377
|
this.applyCaseResult(runId, result);
|
|
409
378
|
const run = this.runs.get(runId);
|
|
410
379
|
if (run !== undefined) {
|
|
411
|
-
|
|
380
|
+
this.persist(run);
|
|
412
381
|
}
|
|
413
382
|
},
|
|
414
383
|
});
|
|
@@ -418,7 +387,7 @@ export class EvalRunStore {
|
|
|
418
387
|
}
|
|
419
388
|
run.status = "completed";
|
|
420
389
|
run.finishedAt = new Date().toISOString();
|
|
421
|
-
|
|
390
|
+
this.persist(run);
|
|
422
391
|
this.logger(
|
|
423
392
|
`[agentkit] eval run ${runId}: done — ${run.summary.passed} passed, ${run.summary.failed} failed`
|
|
424
393
|
);
|
|
@@ -430,7 +399,7 @@ export class EvalRunStore {
|
|
|
430
399
|
run.status = "failed";
|
|
431
400
|
run.finishedAt = new Date().toISOString();
|
|
432
401
|
run.error = error instanceof Error ? error.message : String(error);
|
|
433
|
-
|
|
402
|
+
this.persist(run);
|
|
434
403
|
this.logger(`[agentkit] eval run ${runId}: failed — ${run.error}`);
|
|
435
404
|
} finally {
|
|
436
405
|
if (this.activeRunId === runId) {
|
|
@@ -489,6 +458,23 @@ function markInterruptedRun(run: EvalRunSnapshot): void {
|
|
|
489
458
|
c.error = c.error ?? INTERRUPTED_CASE_ERROR;
|
|
490
459
|
}
|
|
491
460
|
}
|
|
461
|
+
// Reconcile the summary with the repaired case rows so a persisted
|
|
462
|
+
// interrupted batch never claims pre-interruption pass counts.
|
|
463
|
+
let passed = 0;
|
|
464
|
+
let failed = 0;
|
|
465
|
+
for (const c of run.cases) {
|
|
466
|
+
if (c.ok === true) {
|
|
467
|
+
passed += 1;
|
|
468
|
+
} else {
|
|
469
|
+
failed += 1;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
run.summary = {
|
|
473
|
+
passed,
|
|
474
|
+
failed,
|
|
475
|
+
total: run.cases.length,
|
|
476
|
+
done: run.cases.length,
|
|
477
|
+
};
|
|
492
478
|
}
|
|
493
479
|
|
|
494
480
|
function snapshotListConfig(
|
|
@@ -433,14 +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 --dir .
|
|
436
|
+
4. ${CLI_COMMAND_NAME} eval --local --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 [--json] [evalId...]
|
|
443
|
+
Run: ${CLI_COMMAND_NAME} eval --local [--json] [evalId...]
|
|
444
|
+
(omit --local to run against the hosted deployment)
|
|
444
445
|
|
|
445
446
|
See AGENTS.md in the ${PACKAGE_NAME} package for the full loop.
|
|
446
447
|
`);
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* request-targets are rejected so injected headers never leave the configured
|
|
15
15
|
* upstream.
|
|
16
16
|
*
|
|
17
|
-
* When `refreshUpstream` is provided (
|
|
18
|
-
* re-minted shortly before `expiresAt` and once on upstream
|
|
19
|
-
* playground session does not require restarting the CLI.
|
|
17
|
+
* When `refreshUpstream` is provided (e.g. short-lived upstream tokens),
|
|
18
|
+
* credentials are re-minted shortly before `expiresAt` and once on upstream
|
|
19
|
+
* 401 so a long playground session does not require restarting the CLI.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { randomBytes } from "node:crypto";
|
|
@@ -84,8 +84,8 @@ export interface StartPlaygroundProxyArgs {
|
|
|
84
84
|
/** Initial engineAccess expiry (ISO); enables proactive refresh. */
|
|
85
85
|
expiresAt?: string;
|
|
86
86
|
/**
|
|
87
|
-
* Re-mint upstream URL + headers
|
|
88
|
-
*
|
|
87
|
+
* Re-mint upstream URL + headers. Single-flighted across concurrent browser
|
|
88
|
+
* requests; called near expiry and once on 401.
|
|
89
89
|
*/
|
|
90
90
|
refreshUpstream?: () => Promise<PlaygroundUpstreamAccess>;
|
|
91
91
|
/** Bind port; `0` = ephemeral. */
|
|
@@ -48,6 +48,29 @@ export interface ResolvedProdTarget {
|
|
|
48
48
|
expiresAt: string;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when a hosted deployment cannot be resolved. `notFound` is true
|
|
53
|
+
* when the deployment does not exist, so callers can suggest deploying
|
|
54
|
+
* (or running locally) instead of surfacing a bare API error.
|
|
55
|
+
*/
|
|
56
|
+
export class ProdTargetError extends Error {
|
|
57
|
+
constructor(
|
|
58
|
+
message: string,
|
|
59
|
+
readonly options: { slug: string; notFound?: boolean } = { slug: "" }
|
|
60
|
+
) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.name = "ProdTargetError";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get slug(): string {
|
|
66
|
+
return this.options.slug;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get notFound(): boolean {
|
|
70
|
+
return this.options.notFound === true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
/**
|
|
52
75
|
* Look up a running deployment and return its engineAccess URL/headers.
|
|
53
76
|
* Throws {@link Error} with a CLI-ready message on failure.
|
|
@@ -65,8 +88,10 @@ export async function resolveProdTarget(
|
|
|
65
88
|
err,
|
|
66
89
|
});
|
|
67
90
|
if (typeof auth === "number") {
|
|
91
|
+
// Not "--prod": `eval` resolves the hosted deployment by default,
|
|
92
|
+
// without any flag.
|
|
68
93
|
throw new Error(
|
|
69
|
-
`Could not resolve Cursor credentials/team for
|
|
94
|
+
`Could not resolve Cursor credentials/team for the hosted deployment (see above).`
|
|
70
95
|
);
|
|
71
96
|
}
|
|
72
97
|
|
|
@@ -83,8 +108,9 @@ export async function resolveProdTarget(
|
|
|
83
108
|
});
|
|
84
109
|
} catch (error) {
|
|
85
110
|
if (error instanceof DeployApiError) {
|
|
86
|
-
throw new
|
|
87
|
-
`Deployment "${slug}" (team ${auth.teamId}): ${error.message}
|
|
111
|
+
throw new ProdTargetError(
|
|
112
|
+
`Deployment "${slug}" (team ${auth.teamId}): ${error.message}`,
|
|
113
|
+
{ slug, notFound: error.status === 404 }
|
|
88
114
|
);
|
|
89
115
|
}
|
|
90
116
|
throw error;
|
|
@@ -143,6 +169,78 @@ export async function resolveProdTarget(
|
|
|
143
169
|
};
|
|
144
170
|
}
|
|
145
171
|
|
|
172
|
+
export interface ResolvedProdAliasPlayground {
|
|
173
|
+
/** Hosted playground URL on the API alias (SPA owns alias-token sign-in). */
|
|
174
|
+
playgroundUrl: string;
|
|
175
|
+
slug: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Look up a running deployment and return its stable alias playground URL.
|
|
180
|
+
* Does not mint `engineAccess` — the browser signs in with the alias token.
|
|
181
|
+
* Throws {@link Error} with a CLI-ready message on failure.
|
|
182
|
+
*/
|
|
183
|
+
export async function resolveProdAliasPlaygroundUrl(
|
|
184
|
+
options: ResolveProdTargetOptions & { sessionId?: string }
|
|
185
|
+
): Promise<ResolvedProdAliasPlayground> {
|
|
186
|
+
const err =
|
|
187
|
+
options.err ?? ((text: string) => void process.stderr.write(text));
|
|
188
|
+
const slug = resolveProdSlug(options);
|
|
189
|
+
const auth = await resolveDeployAuth({
|
|
190
|
+
team: options.team,
|
|
191
|
+
apiKey: options.apiKey,
|
|
192
|
+
fetchImpl: options.fetchImpl,
|
|
193
|
+
err,
|
|
194
|
+
});
|
|
195
|
+
if (typeof auth === "number") {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Could not resolve Cursor credentials/team for the hosted deployment (see above).`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let detail: DeploymentDetail;
|
|
202
|
+
try {
|
|
203
|
+
detail = await auth.client.getDeployment({
|
|
204
|
+
teamId: auth.teamId,
|
|
205
|
+
slug,
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error instanceof DeployApiError) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`Deployment "${slug}" (team ${auth.teamId}): ${error.message}`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const statusOk = detail.status === "running" || detail.status === "degraded";
|
|
217
|
+
if (!statusOk) {
|
|
218
|
+
const lastError =
|
|
219
|
+
detail.lastError === null || detail.lastError === undefined
|
|
220
|
+
? ""
|
|
221
|
+
: ` ${sanitizeCustomerFacingDetail(detail.lastError)}`;
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Deployment "${slug}" is ${detail.status}, not running.${lastError}\n` +
|
|
224
|
+
`Deploy or wait: \`${CLI} deploy --slug ${slug}\` / \`${CLI} deployment ${slug}\``
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const aliasBase = auth.client.aliasUrl(detail.engineAlias).replace(/\/$/, "");
|
|
229
|
+
const base = `${aliasBase}/playground`;
|
|
230
|
+
const sessionId =
|
|
231
|
+
options.sessionId !== undefined && options.sessionId.trim() !== ""
|
|
232
|
+
? options.sessionId.trim()
|
|
233
|
+
: undefined;
|
|
234
|
+
const playgroundUrl =
|
|
235
|
+
sessionId === undefined
|
|
236
|
+
? base
|
|
237
|
+
: `${base}?sessionId=${encodeURIComponent(sessionId)}`;
|
|
238
|
+
|
|
239
|
+
err(`${stderrPalette().dim(`Using hosted deployment "${slug}".`)}\n`);
|
|
240
|
+
|
|
241
|
+
return { playgroundUrl, slug };
|
|
242
|
+
}
|
|
243
|
+
|
|
146
244
|
/** Strip obvious hosting internals from customer-facing error fragments. */
|
|
147
245
|
function sanitizeCustomerFacingDetail(message: string): string {
|
|
148
246
|
return message
|
package/src/internal/server.ts
CHANGED
|
@@ -362,9 +362,8 @@ export async function startServer(
|
|
|
362
362
|
const evalRuns = new EvalRunStore(
|
|
363
363
|
project.rootDir,
|
|
364
364
|
logger,
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
engine.storage?.asEvalRunPersistence()
|
|
365
|
+
// Playground eval history mirrors onto the defineStorage evals table.
|
|
366
|
+
engine.storage?.evalRuns()
|
|
368
367
|
);
|
|
369
368
|
// Eval history restores with the same startup step as sessions (reads
|
|
370
369
|
// also hydrate lazily, but doing it here surfaces store problems at
|
|
@@ -738,6 +737,9 @@ export async function startServer(
|
|
|
738
737
|
for (const runtime of runtimes) {
|
|
739
738
|
runtime.scheduleRunner.stop();
|
|
740
739
|
runtime.reminderRunner.stop();
|
|
740
|
+
// Before the engines close their storage coordinators, so the
|
|
741
|
+
// interrupted-batch write can still reach the sink.
|
|
742
|
+
runtime.evalRuns.close();
|
|
741
743
|
}
|
|
742
744
|
for (const stop of channelStopHooks) {
|
|
743
745
|
try {
|
|
@@ -204,8 +204,7 @@ export class SessionEngine {
|
|
|
204
204
|
* Runtime for `agent/storage.ts` (`defineStorage`), when
|
|
205
205
|
* authored. All durable state changes funnel through it (session
|
|
206
206
|
* records, event chunks, A/B samples/snapshots); the eval run store
|
|
207
|
-
* attaches via {@link StorageCoordinator.
|
|
208
|
-
* serve start.
|
|
207
|
+
* attaches via {@link StorageCoordinator.evalRuns} at serve start.
|
|
209
208
|
*/
|
|
210
209
|
readonly storage: StorageCoordinator | undefined;
|
|
211
210
|
/** In-flight lazy restores, deduped per channel + continuation key. */
|
|
@@ -261,18 +260,12 @@ export class SessionEngine {
|
|
|
261
260
|
projectRoot: options.project.rootDir,
|
|
262
261
|
logger: this.logger,
|
|
263
262
|
});
|
|
264
|
-
// Domain-specific hooks win; the defineStorage sink is the fallback.
|
|
265
|
-
const persistSamples =
|
|
266
|
-
options.project.abConfig?.persistSamples ??
|
|
267
|
-
this.storage?.asABSamplePersistence();
|
|
268
263
|
this.abCollector = new ABCollector(
|
|
269
264
|
options.project.abs,
|
|
270
265
|
this.logger,
|
|
271
266
|
async (sessionId) => (await this.logs.get(sessionId)).snapshot(0),
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
persistSamples,
|
|
275
|
-
}
|
|
267
|
+
// Samples flow to the defineStorage `abs` table (no-op without one).
|
|
268
|
+
{ persistSample: (sample) => this.storage?.abSample(sample) }
|
|
276
269
|
);
|
|
277
270
|
// Peer and Cursor-account transports are symbolic until the serve host
|
|
278
271
|
// resolves them; the host-side MCP registry starts with the concrete
|
|
@@ -1592,6 +1585,7 @@ export class SessionEngine {
|
|
|
1592
1585
|
return restoredSnapshot;
|
|
1593
1586
|
}
|
|
1594
1587
|
}
|
|
1588
|
+
const abTable = this.project.storage?.abs;
|
|
1595
1589
|
const snapshot = await buildABSnapshot({
|
|
1596
1590
|
experiments: this.project.abs,
|
|
1597
1591
|
agentName: this.project.name,
|
|
@@ -1602,27 +1596,13 @@ export class SessionEngine {
|
|
|
1602
1596
|
logger: this.logger,
|
|
1603
1597
|
config: {
|
|
1604
1598
|
maxPlaygroundSessions,
|
|
1605
|
-
durableSamples:
|
|
1606
|
-
durableSnapshots:
|
|
1599
|
+
durableSamples: abTable !== undefined,
|
|
1600
|
+
durableSnapshots: abTable?.putSnapshot !== undefined,
|
|
1607
1601
|
},
|
|
1608
1602
|
});
|
|
1609
|
-
// Side effect on the read path: playground polls GET /v1/abs ~every 4s
|
|
1610
|
-
//
|
|
1611
|
-
|
|
1612
|
-
if (persistSnapshots !== undefined) {
|
|
1613
|
-
try {
|
|
1614
|
-
await persistSnapshots.save(snapshot, {
|
|
1615
|
-
projectRoot: this.project.rootDir,
|
|
1616
|
-
});
|
|
1617
|
-
} catch (error) {
|
|
1618
|
-
this.logger(
|
|
1619
|
-
`[agentkit] ab persistSnapshots.save threw: ${describeError(error)}`
|
|
1620
|
-
);
|
|
1621
|
-
}
|
|
1622
|
-
} else {
|
|
1623
|
-
// defineStorage fallback (throttled inside the coordinator).
|
|
1624
|
-
this.storage?.abSnapshot(snapshot);
|
|
1625
|
-
}
|
|
1603
|
+
// Side effect on the read path: playground polls GET /v1/abs ~every 4s;
|
|
1604
|
+
// the coordinator throttles the actual table write.
|
|
1605
|
+
this.storage?.abSnapshot(snapshot);
|
|
1626
1606
|
return snapshot;
|
|
1627
1607
|
}
|
|
1628
1608
|
|