@cursor/july 0.1.6 → 0.1.8
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 +73 -19
- package/dist/channels/slack/channel-watch.d.ts +6 -1
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +22 -1
- package/dist/channels/slack/cursor-account.d.ts +4 -0
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +12 -2
- package/dist/channels/slack/defaults.d.ts +10 -5
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +39 -17
- package/dist/channels/slack/dispatch.d.ts +18 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +141 -38
- package/dist/channels/slack/inbound.d.ts +13 -3
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +17 -9
- package/dist/channels/slack/index.d.ts +4 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +4 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +1 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/nudge-carrier.d.ts +19 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
- package/dist/channels/slack/nudge-carrier.js +166 -0
- package/dist/channels/slack/nudge-store.d.ts +61 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -0
- package/dist/channels/slack/nudge-store.js +266 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +74 -25
- package/dist/channels/slack/stop.d.ts +14 -0
- package/dist/channels/slack/stop.d.ts.map +1 -0
- package/dist/channels/slack/stop.js +22 -0
- package/dist/channels/slack/thread-context.d.ts +81 -0
- package/dist/channels/slack/thread-context.d.ts.map +1 -0
- package/dist/channels/slack/thread-context.js +470 -0
- package/dist/channels/slack/types.d.ts +27 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DMboGKUD.js → app.BP8mAVMW.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DevOabKJ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DkEqEnJU.js → VPLocalSearchBox.1FXIZbz8.js} +1 -1
- package/dist/docs/assets/chunks/{theme.ASugdMNm.js → theme.Dm1R02v9.js} +2 -2
- package/dist/docs/assets/example-agents_index.md.CwtWu5ix.js +2 -0
- package/dist/docs/assets/{example-agents_index.md.CRqJlnIf.lean.js → example-agents_index.md.CwtWu5ix.lean.js} +1 -1
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
- package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.Cu3MwM2a.js} +3 -2
- package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.Cu3MwM2a.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +5 -5
- package/dist/docs/example-agents/bugbot.html +5 -5
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +4 -4
- package/dist/docs/example-agents/index.html +6 -6
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +34 -0
- package/dist/docs/example-agents/security-reviewer.html +4 -4
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- 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 +7 -6
- 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 +4 -4
- 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 +4 -4
- 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 +5 -5
- 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 +7 -6
- package/dist/docs/troubleshooting.html +4 -4
- 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/cli-mcp.d.ts +64 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -0
- package/dist/internal/cli-mcp.js +570 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +2 -0
- 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/host-kv.d.ts +17 -0
- package/dist/internal/host-kv.d.ts.map +1 -0
- package/dist/internal/host-kv.js +77 -0
- package/dist/internal/init-project.js +3 -3
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +19 -2
- package/dist/internal/session-engine.d.ts +42 -7
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +247 -51
- package/dist/internal/storage-coordinator.d.ts +18 -1
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +43 -0
- package/dist/playground/assets/index-Bpr4Th4M.js +319 -0
- package/dist/playground/index.html +1 -1
- package/dist/storage-backends/file-kv.d.ts +42 -0
- package/dist/storage-backends/file-kv.d.ts.map +1 -0
- package/dist/storage-backends/file-kv.js +190 -0
- package/dist/storage-backends/postgres-kv.d.ts +77 -0
- package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
- package/dist/storage-backends/postgres-kv.js +180 -0
- package/dist/storage.d.ts +10 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +10 -1
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/example-agents/index.md +14 -9
- package/docs/example-agents/oncall.md +216 -0
- package/docs/guides/slack.md +11 -2
- package/docs/reference/sessions.md +13 -4
- package/docs/storage.md +27 -0
- package/package.json +15 -1
- package/skills/framework-map/SKILL.md +6 -4
- package/src/bin/agent-serve.ts +79 -18
- package/src/channels/slack/channel-watch.ts +28 -1
- package/src/channels/slack/cursor-account.ts +20 -0
- package/src/channels/slack/defaults.ts +51 -20
- package/src/channels/slack/dispatch.ts +215 -40
- package/src/channels/slack/inbound.ts +30 -10
- package/src/channels/slack/index.ts +4 -0
- package/src/channels/slack/live-delivery.ts +1 -0
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/nudge-carrier.ts +182 -0
- package/src/channels/slack/nudge-store.ts +308 -0
- package/src/channels/slack/slack-channel.ts +106 -31
- package/src/channels/slack/stop.ts +25 -0
- package/src/channels/slack/thread-context.ts +572 -0
- package/src/channels/slack/types.ts +27 -0
- package/src/evals.ts +2 -2
- package/src/internal/cli-ax.ts +466 -75
- package/src/internal/cli-mcp.ts +708 -0
- package/src/internal/distribution.ts +2 -0
- package/src/internal/eval-run-store.ts +5 -0
- package/src/internal/evals-client.ts +431 -0
- package/src/internal/host-kv.ts +85 -0
- package/src/internal/init-project.ts +3 -3
- package/src/internal/server.ts +43 -0
- package/src/internal/session-engine.ts +351 -50
- package/src/internal/storage-coordinator.ts +41 -0
- package/src/storage-backends/file-kv.ts +221 -0
- package/src/storage-backends/postgres-kv.ts +248 -0
- package/src/storage.ts +11 -1
- package/src/types.ts +83 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.O6eP7QZr.js +0 -1
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
- package/dist/playground/assets/index-mux9aj5P.js +0 -319
|
@@ -47,6 +47,8 @@ export interface StartEvalRunInput {
|
|
|
47
47
|
tags?: string[];
|
|
48
48
|
bearerToken?: string;
|
|
49
49
|
verbose?: boolean;
|
|
50
|
+
/** Per-case timeout override (same semantics as CLI `--timeout-ms`). */
|
|
51
|
+
timeoutMs?: number;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
export type EvalListConfig = {
|
|
@@ -190,6 +192,7 @@ export class EvalRunStore {
|
|
|
190
192
|
? {}
|
|
191
193
|
: { bearerToken: input.bearerToken }),
|
|
192
194
|
...(input.verbose === undefined ? {} : { verbose: input.verbose }),
|
|
195
|
+
...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
|
|
193
196
|
});
|
|
194
197
|
|
|
195
198
|
return snapshot;
|
|
@@ -316,6 +319,7 @@ export class EvalRunStore {
|
|
|
316
319
|
config: Partial<EvalConfigFile>;
|
|
317
320
|
bearerToken?: string;
|
|
318
321
|
verbose?: boolean;
|
|
322
|
+
timeoutMs?: number;
|
|
319
323
|
}
|
|
320
324
|
): Promise<void> {
|
|
321
325
|
if (this.runs.get(runId) === undefined) {
|
|
@@ -330,6 +334,7 @@ export class EvalRunStore {
|
|
|
330
334
|
? {}
|
|
331
335
|
: { bearerToken: opts.bearerToken }),
|
|
332
336
|
...(opts.verbose === undefined ? {} : { verbose: opts.verbose }),
|
|
337
|
+
...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
|
|
333
338
|
onCaseStart: (id) => {
|
|
334
339
|
const run = this.runs.get(runId);
|
|
335
340
|
if (run === undefined) {
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author-facing durable JSON KV for {@link HostContext.kv}.
|
|
3
|
+
*
|
|
4
|
+
* When `defineStorage` is authored and exposes `get`, values live in that
|
|
5
|
+
* sink under {@link storageKeys.kv}. Otherwise they land as one JSON file
|
|
6
|
+
* per key under `--state-root/kv` (local dogfood / no durable plug-in).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { storageKeys } from "../storage.js";
|
|
13
|
+
import type { HostKvApi, JsonValue } from "../types.js";
|
|
14
|
+
import type { StorageCoordinator } from "./storage-coordinator.js";
|
|
15
|
+
|
|
16
|
+
export interface CreateHostKvApiOptions {
|
|
17
|
+
agentName: string;
|
|
18
|
+
stateRoot: string;
|
|
19
|
+
storage?: StorageCoordinator;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Build the host KV API for one agent process. */
|
|
23
|
+
export function createHostKvApi(options: CreateHostKvApiOptions): HostKvApi {
|
|
24
|
+
const storage = options.storage;
|
|
25
|
+
if (storage?.supportsAuthorKv === true) {
|
|
26
|
+
return {
|
|
27
|
+
get: (key) => storage.kvGet(normalizeAuthorKey(key)),
|
|
28
|
+
put: (key, value) => storage.kvPut(normalizeAuthorKey(key), value),
|
|
29
|
+
delete: (key) => storage.kvDelete(normalizeAuthorKey(key)),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return createFilesystemKvApi({
|
|
33
|
+
agentName: options.agentName,
|
|
34
|
+
stateRoot: options.stateRoot,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createFilesystemKvApi(options: {
|
|
39
|
+
agentName: string;
|
|
40
|
+
stateRoot: string;
|
|
41
|
+
}): HostKvApi {
|
|
42
|
+
const dir = join(options.stateRoot, "kv");
|
|
43
|
+
const filePath = (authorKey: string): string =>
|
|
44
|
+
join(
|
|
45
|
+
dir,
|
|
46
|
+
`${encodeURIComponent(storageKeys.kv(options.agentName, authorKey))}.json`
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
async get(key): Promise<JsonValue | undefined> {
|
|
51
|
+
const authorKey = normalizeAuthorKey(key);
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(
|
|
54
|
+
await readFile(filePath(authorKey), "utf8")
|
|
55
|
+
) as JsonValue;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
async put(key, value): Promise<void> {
|
|
65
|
+
const authorKey = normalizeAuthorKey(key);
|
|
66
|
+
await mkdir(dir, { recursive: true });
|
|
67
|
+
const target = filePath(authorKey);
|
|
68
|
+
const tmp = join(dir, `.tmp-${randomUUID()}`);
|
|
69
|
+
await writeFile(tmp, JSON.stringify(value), "utf8");
|
|
70
|
+
await rename(tmp, target);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async delete(key): Promise<void> {
|
|
74
|
+
const authorKey = normalizeAuthorKey(key);
|
|
75
|
+
await rm(filePath(authorKey), { force: true });
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizeAuthorKey(key: string): string {
|
|
81
|
+
if (typeof key !== "string" || key.trim() === "") {
|
|
82
|
+
throw new Error("host.kv key must be a non-empty string");
|
|
83
|
+
}
|
|
84
|
+
return key;
|
|
85
|
+
}
|
|
@@ -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
|
@@ -423,6 +423,17 @@ export async function startServer(
|
|
|
423
423
|
},
|
|
424
424
|
resolveApproval: (sessionId, callId, decision, auth) =>
|
|
425
425
|
engine.resolveApproval(sessionId, callId, decision, auth),
|
|
426
|
+
hasContinuationSession: (continuationToken) =>
|
|
427
|
+
engine.hasContinuationSession(channel.id, continuationToken),
|
|
428
|
+
isContinuationBusy: (continuationToken) =>
|
|
429
|
+
engine.isContinuationBusy(channel.id, continuationToken),
|
|
430
|
+
getContinuationLastBotMessageTs: (continuationToken) =>
|
|
431
|
+
engine.getContinuationLastBotMessageTs(
|
|
432
|
+
channel.id,
|
|
433
|
+
continuationToken
|
|
434
|
+
),
|
|
435
|
+
interruptContinuation: (continuationToken) =>
|
|
436
|
+
engine.interruptContinuation(channel.id, continuationToken),
|
|
426
437
|
logger,
|
|
427
438
|
};
|
|
428
439
|
try {
|
|
@@ -1082,6 +1093,8 @@ function mountAgentRoutes(
|
|
|
1082
1093
|
const body = await readJsonObject(request);
|
|
1083
1094
|
const filterIds = asStringArray(body?.filterIds);
|
|
1084
1095
|
const tags = asStringArray(body?.tags);
|
|
1096
|
+
const timeoutMs = asPositiveInt(body?.timeoutMs);
|
|
1097
|
+
const verbose = body?.verbose === true;
|
|
1085
1098
|
const authHeader = request.headers.get("authorization");
|
|
1086
1099
|
const bearerToken = authHeader?.toLowerCase().startsWith("bearer ")
|
|
1087
1100
|
? authHeader.slice("bearer ".length).trim()
|
|
@@ -1093,6 +1106,8 @@ function mountAgentRoutes(
|
|
|
1093
1106
|
...(bearerToken === undefined || bearerToken === ""
|
|
1094
1107
|
? {}
|
|
1095
1108
|
: { bearerToken }),
|
|
1109
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
1110
|
+
...(verbose ? { verbose: true } : {}),
|
|
1096
1111
|
});
|
|
1097
1112
|
return jsonResponse(snap, { status: 202 });
|
|
1098
1113
|
} catch (error) {
|
|
@@ -1620,6 +1635,26 @@ async function startCursorSlackRelay(input: {
|
|
|
1620
1635
|
envelope,
|
|
1621
1636
|
send: args.send,
|
|
1622
1637
|
host: args.host,
|
|
1638
|
+
hasContinuationSession: (continuationToken) =>
|
|
1639
|
+
target.runtime.engine.hasContinuationSession(
|
|
1640
|
+
target.channelId,
|
|
1641
|
+
continuationToken
|
|
1642
|
+
),
|
|
1643
|
+
isContinuationBusy: (continuationToken) =>
|
|
1644
|
+
target.runtime.engine.isContinuationBusy(
|
|
1645
|
+
target.channelId,
|
|
1646
|
+
continuationToken
|
|
1647
|
+
),
|
|
1648
|
+
getContinuationLastBotMessageTs: (continuationToken) =>
|
|
1649
|
+
target.runtime.engine.getContinuationLastBotMessageTs(
|
|
1650
|
+
target.channelId,
|
|
1651
|
+
continuationToken
|
|
1652
|
+
),
|
|
1653
|
+
interruptContinuation: (continuationToken) =>
|
|
1654
|
+
target.runtime.engine.interruptContinuation(
|
|
1655
|
+
target.channelId,
|
|
1656
|
+
continuationToken
|
|
1657
|
+
),
|
|
1623
1658
|
});
|
|
1624
1659
|
},
|
|
1625
1660
|
});
|
|
@@ -1896,6 +1931,14 @@ function asStringArray(value: unknown): string[] | undefined {
|
|
|
1896
1931
|
return out.length === 0 ? undefined : out;
|
|
1897
1932
|
}
|
|
1898
1933
|
|
|
1934
|
+
function asPositiveInt(value: unknown): number | undefined {
|
|
1935
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1936
|
+
return undefined;
|
|
1937
|
+
}
|
|
1938
|
+
const n = Math.floor(value);
|
|
1939
|
+
return n > 0 ? n : undefined;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1899
1942
|
async function assertReminderReadable(input: {
|
|
1900
1943
|
engine: SessionEngine;
|
|
1901
1944
|
reminder: ReminderInfo;
|