@whiteroom-ai/cli 1.1.0 → 2.0.0
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/README.md +54 -0
- package/contract/catalog.json +197 -0
- package/dist/index.js +12 -15
- package/dist/index.js.map +1 -1
- package/dist/legacy-args.d.ts +4 -0
- package/dist/legacy-args.js +17 -0
- package/dist/legacy-args.js.map +1 -0
- package/dist/practice/adapter.d.ts +59 -0
- package/dist/practice/adapter.js +169 -0
- package/dist/practice/adapter.js.map +1 -0
- package/dist/practice/catalog.d.ts +16 -0
- package/dist/practice/catalog.js +6 -0
- package/dist/practice/catalog.js.map +1 -0
- package/dist/practice/engine.d.ts +50 -0
- package/dist/practice/engine.js +147 -0
- package/dist/practice/engine.js.map +1 -0
- package/dist/practice/index.d.ts +2 -0
- package/dist/practice/index.js +72 -0
- package/dist/practice/index.js.map +1 -0
- package/dist/practice/preflight.d.ts +58 -0
- package/dist/practice/preflight.js +185 -0
- package/dist/practice/preflight.js.map +1 -0
- package/dist/practice/run.d.ts +11 -0
- package/dist/practice/run.js +374 -0
- package/dist/practice/run.js.map +1 -0
- package/dist/practice/text.d.ts +91 -0
- package/dist/practice/text.js +140 -0
- package/dist/practice/text.js.map +1 -0
- package/package.json +10 -3
- package/starters/wr-adapter.mjs +104 -0
- package/starters/wr_adapter.py +136 -0
- package/src/index.ts +0 -144
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
// `whiteroom practice run` (spec §4 Claim / Practice tasks, §6, §8, §10):
|
|
2
|
+
// preflight, claim, then the practice tasks and control checks the engine
|
|
3
|
+
// hands out, one at a time, until it returns the result.
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { childEnv, runAdapter } from "./adapter.js";
|
|
7
|
+
import { Engine, pace, REDIRECTED, sleep, Stopped, Unreachable } from "./engine.js";
|
|
8
|
+
import { CATALOG } from "./catalog.js";
|
|
9
|
+
import { adapterLabel, adapterProblem, chatCompletions, firstLineOfStdin, hiddenPrompt, Interrupted, isKeyVar, isProvider, isShortTermBedrockKey, keyFromEnv, keyHeader, keyVarOf, nodeTooOld, providerByShape, RUN_CODE, targetOrigin, } from "./preflight.js";
|
|
10
|
+
import { copy, ending, hideSecret, results, say, warn } from "./text.js";
|
|
11
|
+
const HEARTBEAT_MS = 30_000;
|
|
12
|
+
/** A practice task WhiteRoom dropped (a restart, a failed capture): ask for it again, it doesn't count (§2 rule 4). */
|
|
13
|
+
const RESTART = new Set(["attempt_discarded", "not_found"]);
|
|
14
|
+
/** The final answer WhiteRoom takes: 32 Ki characters, inside its 64 KiB body limit. */
|
|
15
|
+
export function finalText(s) {
|
|
16
|
+
let out = s.slice(0, 32 * 1024);
|
|
17
|
+
while (Buffer.byteLength(out) > 60_000)
|
|
18
|
+
out = out.slice(0, Math.floor(out.length * 0.9));
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
export const STARTERS_DIR = new URL("../../starters/", import.meta.url);
|
|
22
|
+
/** The sample agent is the JS starter adapter (§5 Agents). */
|
|
23
|
+
const SAMPLE_AGENT = fileURLToPath(new URL("wr-adapter.mjs", STARTERS_DIR));
|
|
24
|
+
const VERSION = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version;
|
|
25
|
+
/**
|
|
26
|
+
* Any refusal after a good claim ends the test with exit 3 (§8, §10 "Post-claim 409s").
|
|
27
|
+
* 5xx and network errors never get here: Engine.post retries them or throws Unreachable.
|
|
28
|
+
*/
|
|
29
|
+
function sessionEnding(r) {
|
|
30
|
+
const reason = r.body?.reason;
|
|
31
|
+
// The run already finished (WhiteRoom finished it meanwhile): its result, not a refusal. Without one, tasks/next replays it.
|
|
32
|
+
if (r.body?.done || reason === "finished")
|
|
33
|
+
return r.body?.report ? results(r.body.report) : { ...ending(3, copy.finished(r.body?.verdict)), result: true, replay: true };
|
|
34
|
+
if (r.status === REDIRECTED)
|
|
35
|
+
return ending(3, copy.redirected);
|
|
36
|
+
if (reason === "engine_update")
|
|
37
|
+
return ending(3, copy.engineUpdate(Number(r.body.tasksFinished) || 0));
|
|
38
|
+
if (reason === "ended_by_owner")
|
|
39
|
+
return ending(3, copy.endedByOwner);
|
|
40
|
+
if (reason === "expired")
|
|
41
|
+
return ending(3, copy.expired);
|
|
42
|
+
if (reason === "call_cap")
|
|
43
|
+
return ending(3, copy.callCap);
|
|
44
|
+
if (reason === "no_model_calls")
|
|
45
|
+
return ending(3, copy.noModelCalls);
|
|
46
|
+
if (reason === "engine_error")
|
|
47
|
+
return ending(3, copy.engineError);
|
|
48
|
+
if (r.status === 401)
|
|
49
|
+
return ending(3, copy.sessionGone);
|
|
50
|
+
if (r.status === 429)
|
|
51
|
+
return ending(3, copy.busy);
|
|
52
|
+
if (r.status === 403)
|
|
53
|
+
return ending(3, copy.refused);
|
|
54
|
+
if (r.status === 404 && !reason)
|
|
55
|
+
return ending(3, copy.unavailable);
|
|
56
|
+
return ending(3, copy.cliOutdated); // A 4xx this CLI doesn't know: it's older than the server.
|
|
57
|
+
}
|
|
58
|
+
const print = (e, out = say) => {
|
|
59
|
+
for (const l of e.lines)
|
|
60
|
+
out(l);
|
|
61
|
+
return e.code;
|
|
62
|
+
};
|
|
63
|
+
/** A refusal on stderr; a result (the run had already finished) on stdout. */
|
|
64
|
+
const fail = (e) => print(e, e.result ? say : warn);
|
|
65
|
+
export async function practiceRun(a) {
|
|
66
|
+
// ── Preflight: nothing is sent until every check passes ──
|
|
67
|
+
const old = nodeTooOld();
|
|
68
|
+
if (old)
|
|
69
|
+
return fail(ending(2, old));
|
|
70
|
+
if (process.platform === "win32")
|
|
71
|
+
return fail(ending(2, copy.windows));
|
|
72
|
+
if (process.env.WHITEROOM_URL)
|
|
73
|
+
say(copy.ignoringUrl);
|
|
74
|
+
const origin = targetOrigin(a.url);
|
|
75
|
+
if (!origin)
|
|
76
|
+
return fail(ending(2, copy.badUrl));
|
|
77
|
+
if (a.adapter !== undefined) {
|
|
78
|
+
const why = adapterProblem(a.adapter);
|
|
79
|
+
if (why)
|
|
80
|
+
return fail(ending(2, copy.adapterMissing(a.adapter, why)));
|
|
81
|
+
}
|
|
82
|
+
// The user's own command runs through their shell as typed; nothing from WhiteRoom is ever put into it (§9.4).
|
|
83
|
+
const launch = a.adapter !== undefined ? { shell: a.adapter } : { file: process.execPath, args: [SAMPLE_AGENT] };
|
|
84
|
+
if (a.keyEnv !== undefined && !isKeyVar(a.keyEnv))
|
|
85
|
+
return fail(ending(2, copy.badKeyEnv(a.keyEnv)));
|
|
86
|
+
const fromEnv = keyFromEnv(process.env, a.keyEnv);
|
|
87
|
+
if (Array.isArray(fromEnv))
|
|
88
|
+
return fail(ending(2, copy.bothKeys(fromEnv)));
|
|
89
|
+
let choice = fromEnv;
|
|
90
|
+
let code = "";
|
|
91
|
+
try {
|
|
92
|
+
if (a.codeStdin)
|
|
93
|
+
code = await firstLineOfStdin();
|
|
94
|
+
if (!choice) {
|
|
95
|
+
if (!process.stdin.isTTY)
|
|
96
|
+
return fail(ending(2, copy.noKey(a.keyEnv)));
|
|
97
|
+
const pasted = await hiddenPrompt(copy.needKey);
|
|
98
|
+
if (!pasted)
|
|
99
|
+
return fail(ending(2, copy.noKey(a.keyEnv)));
|
|
100
|
+
choice = { from: null, key: pasted };
|
|
101
|
+
}
|
|
102
|
+
if (!a.codeStdin) {
|
|
103
|
+
if (!process.stdin.isTTY)
|
|
104
|
+
return fail(ending(2, copy.noTerminal));
|
|
105
|
+
// A malformed paste uses no try: ask again, up to 3 times.
|
|
106
|
+
for (let i = 0; i < 3 && !RUN_CODE.test(code); i++) {
|
|
107
|
+
if (i)
|
|
108
|
+
warn(copy.badCode);
|
|
109
|
+
code = await hiddenPrompt(copy.needCode);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
if (err instanceof Interrupted)
|
|
115
|
+
return fail(ending(3, copy.stoppedEarly));
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
if (!RUN_CODE.test(code))
|
|
119
|
+
return fail(ending(2, copy.badCode));
|
|
120
|
+
const { key, from } = choice;
|
|
121
|
+
hideSecret(key);
|
|
122
|
+
hideSecret(code);
|
|
123
|
+
// WhiteRoom refuses these without using a try; saying so here sends nothing.
|
|
124
|
+
if (isShortTermBedrockKey(key))
|
|
125
|
+
return fail(ending(2, copy.shortTermKey));
|
|
126
|
+
if (from)
|
|
127
|
+
say(copy.keyInUse(from, key));
|
|
128
|
+
say(copy.host(new URL(origin).host));
|
|
129
|
+
// ── Claim: the key is checked against the fleet's; a mismatch uses a try and nothing else ──
|
|
130
|
+
const engine = new Engine(origin);
|
|
131
|
+
let claim;
|
|
132
|
+
try {
|
|
133
|
+
claim = await engine.post("/api/readiness/claim", {
|
|
134
|
+
code, key, cli: { version: VERSION, adapter: a.adapter === undefined ? "sample agent" : adapterLabel(a.adapter) },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (err instanceof Unreachable)
|
|
139
|
+
return fail(ending(3, copy.unreachable));
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
if (claim.status === 401 && claim.body?.reason === "key_mismatch") {
|
|
143
|
+
const b = claim.body;
|
|
144
|
+
const left = Number(b.triesLeft) || 0;
|
|
145
|
+
if (left <= 0)
|
|
146
|
+
return fail(ending(2, copy.lastTry));
|
|
147
|
+
// A run on a Fleet key provider key (R7) names that key's provider and last four.
|
|
148
|
+
const keyProvider = b.provider;
|
|
149
|
+
if (isProvider(keyProvider)) {
|
|
150
|
+
const hint = typeof b.keyHint === "string" && /^[\w-]{1,8}$/.test(b.keyHint) && b.keyHint !== "none" ? b.keyHint : null;
|
|
151
|
+
return fail(ending(2, copy.providerKeyMismatch(from, key, CATALOG.providers[keyProvider].label, hint)));
|
|
152
|
+
}
|
|
153
|
+
const fleet = typeof b.fleetName === "string" && b.fleetName.trim() ? b.fleetName.trim().slice(0, 100) : null;
|
|
154
|
+
return fail(ending(2, copy.keyMismatch(from ?? keyVarOf(providerByShape(key)), left, fleet)));
|
|
155
|
+
}
|
|
156
|
+
if (claim.status === 400 && claim.body?.reason === "short_term_key")
|
|
157
|
+
return fail(ending(2, copy.shortTermKey));
|
|
158
|
+
if (claim.status === REDIRECTED)
|
|
159
|
+
return fail(ending(2, copy.redirected));
|
|
160
|
+
if (claim.status === 410)
|
|
161
|
+
return fail(ending(2, copy.codeInvalid));
|
|
162
|
+
if (claim.status === 429)
|
|
163
|
+
return fail(ending(3, copy.busy));
|
|
164
|
+
// The owner ended the test while this claim was on its way.
|
|
165
|
+
if (claim.status === 409 && claim.body?.reason === "ended_by_owner")
|
|
166
|
+
return fail(ending(3, copy.endedBeforeStart));
|
|
167
|
+
if (claim.status === 404)
|
|
168
|
+
return fail(ending(2, copy.unavailable));
|
|
169
|
+
if (claim.status !== 200 || typeof claim.body?.session !== "string")
|
|
170
|
+
return fail(ending(2, copy.cliOutdated));
|
|
171
|
+
// The provider the run's calls go to decides the adapter's key variable, the header and the API.
|
|
172
|
+
const runProvider = claim.body.run?.provider;
|
|
173
|
+
if (!isProvider(runProvider))
|
|
174
|
+
return fail(ending(2, copy.cliOutdated));
|
|
175
|
+
const provider = runProvider;
|
|
176
|
+
engine.session = claim.body.session;
|
|
177
|
+
hideSecret(engine.session);
|
|
178
|
+
const runs = Number(claim.body.run?.runs) || 1;
|
|
179
|
+
// An engine before practiceTasks counts every step as a practice task.
|
|
180
|
+
const claimTasks = Number(claim.body.run?.tasks) || 0;
|
|
181
|
+
const practiceTasks = Number(claim.body.run?.practiceTasks ?? claim.body.run?.tasks) || 0;
|
|
182
|
+
say(copy.started(practiceTasks, runs, claim.body.run?.controls === true));
|
|
183
|
+
// The runs whose control checks have started: progress counts them as steps, the task line doesn't.
|
|
184
|
+
const controlRuns = new Set();
|
|
185
|
+
// ── From here the session is live: Ctrl+C twice, a signal, or WhiteRoom (heartbeat) can stop it ──
|
|
186
|
+
const stop = new AbortController();
|
|
187
|
+
engine.signal = stop.signal;
|
|
188
|
+
const halt = { why: null, cancel: false };
|
|
189
|
+
const stopWith = (e, cancel) => {
|
|
190
|
+
if (!halt.why)
|
|
191
|
+
Object.assign(halt, { why: e, cancel });
|
|
192
|
+
stop.abort();
|
|
193
|
+
};
|
|
194
|
+
let pressed = 0;
|
|
195
|
+
const onSigint = () => (++pressed === 1 ? warn(copy.pressAgain) : stopWith(ending(3, copy.cancelled), true));
|
|
196
|
+
const onTerm = () => stopWith(ending(3, copy.cancelled), true);
|
|
197
|
+
process.on("SIGINT", onSigint);
|
|
198
|
+
process.on("SIGTERM", onTerm);
|
|
199
|
+
process.on("SIGHUP", onTerm);
|
|
200
|
+
let beating = false;
|
|
201
|
+
const heartbeat = setInterval(() => {
|
|
202
|
+
if (beating)
|
|
203
|
+
return;
|
|
204
|
+
beating = true;
|
|
205
|
+
engine.post("/api/readiness/heartbeat", {}, { retryNetwork: true })
|
|
206
|
+
.then((r) => (r.status !== 200 || r.body?.done) && stopWith(sessionEnding(r), false))
|
|
207
|
+
.catch((err) => err instanceof Unreachable && stopWith(ending(3, copy.unreachable), false))
|
|
208
|
+
.finally(() => { beating = false; });
|
|
209
|
+
}, HEARTBEAT_MS);
|
|
210
|
+
heartbeat.unref();
|
|
211
|
+
/** The key only ever goes to the host the user chose. */
|
|
212
|
+
const ourHost = (url) => {
|
|
213
|
+
try {
|
|
214
|
+
return new URL(String(url)).origin === origin;
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
/** One practice task through the adapter. An Ending stops the test; null asks for the next task. */
|
|
221
|
+
const practiceTask = async (t, p) => {
|
|
222
|
+
if (!ourHost(t.baseUrl))
|
|
223
|
+
return ending(3, copy.otherHost);
|
|
224
|
+
const deadline = Date.parse(t.deadline);
|
|
225
|
+
if (!Number.isFinite(deadline))
|
|
226
|
+
return ending(3, copy.cliOutdated);
|
|
227
|
+
const run = runs > 1 ? ` (run ${t.runIndex + 1} of ${runs})` : "";
|
|
228
|
+
// Counted without the control-check steps, as the start line says ("5 practice tasks and a check of your controls").
|
|
229
|
+
// The progress total is the plan's every step; when it's the plan the claim described, count its practice tasks only.
|
|
230
|
+
const total = p.total === claimTasks && practiceTasks ? practiceTasks : p.total;
|
|
231
|
+
say(`Practice task ${p.done + 1 - (total === p.total ? 0 : controlRuns.size)} of ${total}: ${CATALOG.taskNames[t.scenario] ?? "Practice task"}${run}`);
|
|
232
|
+
const out = await runAdapter({
|
|
233
|
+
launch, env: childEnv(process.env, keyVarOf(provider), key), deadline, signal: stop.signal,
|
|
234
|
+
message: {
|
|
235
|
+
type: "task", task: t.prompt, tools: t.tools, headers: t.headers,
|
|
236
|
+
model: typeof t.model === "string" && t.model ? t.model : null,
|
|
237
|
+
// OpenAI SDKs (Azure OpenAI's too) take a base URL that ends in /v1.
|
|
238
|
+
base_url: chatCompletions(provider) ? `${t.baseUrl}/v1` : t.baseUrl,
|
|
239
|
+
},
|
|
240
|
+
tool: async (call) => {
|
|
241
|
+
const r = await engine.post("/api/readiness/tools", { attemptId: t.attemptId, ...call }).catch(() => null);
|
|
242
|
+
if (!r)
|
|
243
|
+
return { end: "lost" };
|
|
244
|
+
if (r.status === 200)
|
|
245
|
+
return { result: r.body?.result ?? null };
|
|
246
|
+
const reason = r.body?.reason;
|
|
247
|
+
return { end: RESTART.has(reason) ? "restart" : reason === "expired" ? "deadline" : reason === "tool_limit" ? "tool_limit" : r.status === 400 ? "bad_tool" : sessionEnding(r) };
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
if (out.kind === "stopped")
|
|
251
|
+
return null;
|
|
252
|
+
const why = out.kind === "ended" ? out.by : out.kind;
|
|
253
|
+
if (typeof why === "object")
|
|
254
|
+
return why;
|
|
255
|
+
if (why === "restart" || why === "lost")
|
|
256
|
+
return warn(why === "lost" ? copy.lostTask : copy.restart), null;
|
|
257
|
+
const send = (output, ended) => engine.post(`/api/readiness/tasks/${encodeURIComponent(t.attemptId)}/final`, { output, ended }).catch(() => null);
|
|
258
|
+
const final = async (output, ended) => {
|
|
259
|
+
const r = await send(output, ended);
|
|
260
|
+
if (!r)
|
|
261
|
+
return warn(copy.lostTask), null;
|
|
262
|
+
if (r.status === 200)
|
|
263
|
+
return null;
|
|
264
|
+
if (RESTART.has(r.body?.reason))
|
|
265
|
+
return warn(copy.restart), null;
|
|
266
|
+
return sessionEnding(r);
|
|
267
|
+
};
|
|
268
|
+
if (out.kind === "final")
|
|
269
|
+
return final(finalText(out.output), "final");
|
|
270
|
+
if (why === "deadline")
|
|
271
|
+
return warn(copy.deadline), final(null, "deadline");
|
|
272
|
+
// The adapter crashed or broke the protocol: kept as this task's result, unless WhiteRoom dropped the task
|
|
273
|
+
// (it runs again); then the test ends with a result over the tasks done so far (exit 3).
|
|
274
|
+
const r = await send(null, "adapter_exited");
|
|
275
|
+
if (r && r.status !== 200)
|
|
276
|
+
return RESTART.has(r.body?.reason) ? (warn(copy.restart), null) : sessionEnding(r);
|
|
277
|
+
const c = await engine.cancel(true);
|
|
278
|
+
if (c && c.status !== 200 && c.body?.reason === "finished")
|
|
279
|
+
return sessionEnding(c); // It finished meanwhile: its result wins.
|
|
280
|
+
const kept = c?.status === 200 && c.body?.lifecycle === "finished" ? copy.keptResults : copy.keptUnconfirmed;
|
|
281
|
+
if (out.kind === "exited")
|
|
282
|
+
return ending(3, ...copy.adapterCrashed(out.how, out.tail), kept);
|
|
283
|
+
return ending(3, copy.adapterBroke(out.kind === "broke" ? out.why : why === "tool_limit" ? copy.tooManyTools : copy.badTool), kept);
|
|
284
|
+
};
|
|
285
|
+
/** The control checks: each call WhiteRoom plans, sent with the key, its response reported back (§6). */
|
|
286
|
+
const controlChecks = async (c) => {
|
|
287
|
+
if (!ourHost(c.baseUrl))
|
|
288
|
+
return ending(3, copy.otherHost);
|
|
289
|
+
controlRuns.add(c.runIndex);
|
|
290
|
+
say(`Checking your controls${runs > 1 ? ` (run ${c.runIndex + 1} of ${runs})` : ""}`);
|
|
291
|
+
let result = null;
|
|
292
|
+
for (;;) {
|
|
293
|
+
const r = await engine.post("/api/readiness/controls/next", { attemptId: c.attemptId, result }).catch(() => null);
|
|
294
|
+
if (stop.signal.aborted)
|
|
295
|
+
return null;
|
|
296
|
+
// Lost or dropped: tasks/next starts the control checks again. Not safe to resend `result`.
|
|
297
|
+
if (!r)
|
|
298
|
+
return warn(copy.lostTask), null;
|
|
299
|
+
if (r.status !== 200)
|
|
300
|
+
return RESTART.has(r.body?.reason) || r.body?.reason === "expired" ? (warn(copy.restart), null) : sessionEnding(r);
|
|
301
|
+
if (r.body?.done)
|
|
302
|
+
return null;
|
|
303
|
+
const q = r.body?.request;
|
|
304
|
+
if (!q || typeof q.path !== "string")
|
|
305
|
+
return ending(3, copy.cliOutdated);
|
|
306
|
+
if (!ourHost(q.url))
|
|
307
|
+
return ending(3, copy.otherHost);
|
|
308
|
+
await sleep(Math.min(Math.max(Number(q.delayMs) || 0, 0), 120_000), stop.signal);
|
|
309
|
+
if (stop.signal.aborted)
|
|
310
|
+
return null;
|
|
311
|
+
const headers = {};
|
|
312
|
+
for (const [h, v] of Object.entries(q.headers ?? {}))
|
|
313
|
+
if (!/^(authorization|x-api-key|api-key)$/i.test(h))
|
|
314
|
+
headers[h] = String(v);
|
|
315
|
+
Object.assign(headers, keyHeader(provider, key));
|
|
316
|
+
await pace();
|
|
317
|
+
try {
|
|
318
|
+
const res = await fetch(q.url, { method: "POST", headers, body: JSON.stringify(q.body), redirect: "error", signal: AbortSignal.timeout(60_000) });
|
|
319
|
+
const wr = (await res.json().catch(() => null))?.whiteroom;
|
|
320
|
+
const str = (v, max) => (typeof v === "string" ? v.slice(0, max) : null);
|
|
321
|
+
result = { status: res.status, reason: str(wr?.reason, 64), ruleId: str(wr?.ruleId, 128) };
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// A network error or a redirect (never followed, so the key stays on this host):
|
|
325
|
+
// not a result, so WhiteRoom calls the check a test problem.
|
|
326
|
+
result = { status: 0, reason: null, ruleId: null };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
/** Shows how the test ended; a finished run whose reply had no result gets it from tasks/next (one try). */
|
|
331
|
+
const end = async (e) => {
|
|
332
|
+
if (e.replay) {
|
|
333
|
+
engine.signal = null;
|
|
334
|
+
const r = await engine.post("/api/readiness/tasks/next", { key }).catch(() => null);
|
|
335
|
+
if (r?.status === 200 && r.body?.done && r.body.report)
|
|
336
|
+
return print(results(r.body.report));
|
|
337
|
+
}
|
|
338
|
+
return fail(e);
|
|
339
|
+
};
|
|
340
|
+
// ── The practice tasks, until the result ──
|
|
341
|
+
try {
|
|
342
|
+
while (!halt.why) {
|
|
343
|
+
let next;
|
|
344
|
+
try {
|
|
345
|
+
next = await engine.post("/api/readiness/tasks/next", { key }, { retryNetwork: true });
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
if (err instanceof Stopped)
|
|
349
|
+
break;
|
|
350
|
+
if (err instanceof Unreachable)
|
|
351
|
+
return fail(ending(3, copy.unreachable));
|
|
352
|
+
throw err;
|
|
353
|
+
}
|
|
354
|
+
if (next.status !== 200 || next.body?.done)
|
|
355
|
+
return end(sessionEnding(next));
|
|
356
|
+
const b = next.body;
|
|
357
|
+
const e = b?.task ? await practiceTask(b.task, b.progress) : b?.controls ? await controlChecks(b.controls) : ending(3, copy.cliOutdated);
|
|
358
|
+
// A result wins over how the session stopped meanwhile.
|
|
359
|
+
if (e && (!halt.why || e.result))
|
|
360
|
+
return end(e);
|
|
361
|
+
}
|
|
362
|
+
// Ctrl+C or a signal ends the run, so the dashboard doesn't wait for this computer.
|
|
363
|
+
if (halt.cancel)
|
|
364
|
+
await engine.cancel();
|
|
365
|
+
return end(halt.why);
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
clearInterval(heartbeat);
|
|
369
|
+
process.off("SIGINT", onSigint);
|
|
370
|
+
process.off("SIGTERM", onTerm);
|
|
371
|
+
process.off("SIGHUP", onTerm);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/practice/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export declare const DASHBOARD = "https://app.whiteroom.tech/sandbox";
|
|
2
|
+
/** Remembers a secret (provider key, run code, session) so `clean` never prints it. */
|
|
3
|
+
export declare function hideSecret(s: string): void;
|
|
4
|
+
/** Text safe to print: no terminal control characters, no known secret. */
|
|
5
|
+
export declare function clean(s: string): string;
|
|
6
|
+
export declare const say: (s: string) => boolean;
|
|
7
|
+
export declare const warn: (s: string) => boolean;
|
|
8
|
+
export declare const plural: (n: number, one: string, many?: string) => string;
|
|
9
|
+
/** A way the command ends: the exit code (§8) and what to print. */
|
|
10
|
+
export interface Ending {
|
|
11
|
+
code: 0 | 1 | 2 | 3;
|
|
12
|
+
lines: string[];
|
|
13
|
+
/** The test's result (printed on stdout), not a refusal. */
|
|
14
|
+
result?: true;
|
|
15
|
+
/** The run finished, but the reply didn't carry its result: ask tasks/next for it. */
|
|
16
|
+
replay?: true;
|
|
17
|
+
}
|
|
18
|
+
export declare const ending: (code: Ending["code"], ...lines: string[]) => Ending;
|
|
19
|
+
export declare const copy: {
|
|
20
|
+
ignoringUrl: string;
|
|
21
|
+
keyInUse: (v: string, key: string) => string;
|
|
22
|
+
host: (host: string) => string;
|
|
23
|
+
/** `tasks`: the practice tasks over all runs; `controls`: whether a control check makes calls. */
|
|
24
|
+
started: (tasks: number, runs: number, controls: boolean) => string;
|
|
25
|
+
restart: string;
|
|
26
|
+
lostTask: string;
|
|
27
|
+
offline: string;
|
|
28
|
+
deadline: string;
|
|
29
|
+
pressAgain: string;
|
|
30
|
+
badCode: string;
|
|
31
|
+
needCode: string;
|
|
32
|
+
needKey: string;
|
|
33
|
+
bothKeys: (vars: string[]) => string;
|
|
34
|
+
noKey: (keyEnv?: string) => string;
|
|
35
|
+
badKeyEnv: (v: string) => string;
|
|
36
|
+
shortTermKey: string;
|
|
37
|
+
node: (v: string) => string;
|
|
38
|
+
windows: string;
|
|
39
|
+
badUrl: string;
|
|
40
|
+
adapterMissing: (cmd: string, why: string) => string;
|
|
41
|
+
keyMismatch: (v: string, left: number, fleet?: string | null) => string;
|
|
42
|
+
/** §17: a run on a Fleet key provider key names that key as Fleet key does; a new test is the way to pick another. */
|
|
43
|
+
providerKeyMismatch: (from: string | null, key: string, provider: string, hint: string | null) => string;
|
|
44
|
+
lastTry: string;
|
|
45
|
+
codeInvalid: string;
|
|
46
|
+
busy: string;
|
|
47
|
+
unavailable: string;
|
|
48
|
+
unreachable: string;
|
|
49
|
+
cliOutdated: string;
|
|
50
|
+
redirected: string;
|
|
51
|
+
refused: string;
|
|
52
|
+
otherHost: string;
|
|
53
|
+
engineUpdate: (kept: number) => string;
|
|
54
|
+
endedByOwner: string;
|
|
55
|
+
endedBeforeStart: string;
|
|
56
|
+
expired: string;
|
|
57
|
+
callCap: string;
|
|
58
|
+
noModelCalls: string;
|
|
59
|
+
engineError: string;
|
|
60
|
+
finished: (verdict: unknown) => string;
|
|
61
|
+
sessionGone: string;
|
|
62
|
+
cancelled: string;
|
|
63
|
+
adapterCrashed: (code: string, tail: string[]) => string[];
|
|
64
|
+
adapterBroke: (why: string) => string;
|
|
65
|
+
keptResults: string;
|
|
66
|
+
keptUnconfirmed: string;
|
|
67
|
+
tooManyTools: string;
|
|
68
|
+
badTool: string;
|
|
69
|
+
noTerminal: string;
|
|
70
|
+
stoppedEarly: string;
|
|
71
|
+
};
|
|
72
|
+
export interface Check {
|
|
73
|
+
id: string;
|
|
74
|
+
label: string;
|
|
75
|
+
scenario?: string;
|
|
76
|
+
status: "passed" | "failed" | "not_tested" | "unsupported" | "error";
|
|
77
|
+
required: boolean;
|
|
78
|
+
testProblem?: boolean;
|
|
79
|
+
summary?: string | null;
|
|
80
|
+
fix?: string | null;
|
|
81
|
+
mode?: string | null;
|
|
82
|
+
}
|
|
83
|
+
export interface Report {
|
|
84
|
+
verdict: "passed" | "not_passed" | "incomplete";
|
|
85
|
+
runs: number;
|
|
86
|
+
sample: boolean;
|
|
87
|
+
scope: string[] | null;
|
|
88
|
+
checks: Check[];
|
|
89
|
+
}
|
|
90
|
+
/** The results screen in the terminal, and its exit code: 0 Passed, 1 Didn't pass, 3 Didn't finish. */
|
|
91
|
+
export declare function results(r: Report): Ending;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// What the practice commands print (spec §8, §10, Appendix D). Plain words
|
|
2
|
+
// only: no internal names, no engine reason codes. Every way a run ends
|
|
3
|
+
// names the dashboard and the button to click there. Task, status, provider
|
|
4
|
+
// and refusal words are the engine's catalog's.
|
|
5
|
+
import { CATALOG } from "./catalog.js";
|
|
6
|
+
export const DASHBOARD = "https://app.whiteroom.tech/sandbox";
|
|
7
|
+
const KEY_VARS = Object.values(CATALOG.providers).map((p) => p.keyVar);
|
|
8
|
+
/** Control, C1 and bidi characters, which could rewrite what the terminal shows. Tabs and newlines stay. */
|
|
9
|
+
const UNSAFE = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
|
|
10
|
+
const secrets = new Set();
|
|
11
|
+
/** Remembers a secret (provider key, run code, session) so `clean` never prints it. */
|
|
12
|
+
export function hideSecret(s) {
|
|
13
|
+
if (s.length >= 8)
|
|
14
|
+
secrets.add(s);
|
|
15
|
+
}
|
|
16
|
+
/** Text safe to print: no terminal control characters, no known secret. */
|
|
17
|
+
export function clean(s) {
|
|
18
|
+
let out = s.replace(UNSAFE, "");
|
|
19
|
+
for (const x of secrets)
|
|
20
|
+
out = out.split(x).join("[hidden]");
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
export const say = (s) => process.stdout.write(clean(s) + "\n");
|
|
24
|
+
export const warn = (s) => process.stderr.write(clean(s) + "\n");
|
|
25
|
+
export const plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
26
|
+
/** "A, B and C". */
|
|
27
|
+
const list = (xs, and = "and") => (xs.length > 1 ? `${xs.slice(0, -1).join(", ")} ${and} ${xs[xs.length - 1]}` : xs.join(""));
|
|
28
|
+
const button = (name) => `In the dashboard (${DASHBOARD}), click ${name}`;
|
|
29
|
+
export const ending = (code, ...lines) => ({ code, lines });
|
|
30
|
+
export const copy = {
|
|
31
|
+
ignoringUrl: "Ignoring WHITEROOM_URL: practice tests always run on WhiteRoom.",
|
|
32
|
+
keyInUse: (v, key) => `Using the key in ${v} (ends ${key.slice(-4)}).`,
|
|
33
|
+
host: (host) => `Sending your key to ${host} to start the practice test.`,
|
|
34
|
+
/** `tasks`: the practice tasks over all runs; `controls`: whether a control check makes calls. */
|
|
35
|
+
started: (tasks, runs, controls) => {
|
|
36
|
+
const perRun = Math.round(tasks / runs);
|
|
37
|
+
const run = [perRun > 0 && plural(perRun, "practice task"), controls && "a check of your controls"].filter(Boolean).join(" and ");
|
|
38
|
+
return `Practice test started: ${runs > 1 ? `${runs} runs, each ${run}` : run}. Keep this terminal open and your computer awake.`;
|
|
39
|
+
},
|
|
40
|
+
restart: "WhiteRoom restarted this practice task, so it starts again. This doesn't count against your agent.",
|
|
41
|
+
lostTask: "Lost contact with WhiteRoom during this practice task, so it starts again. This doesn't count against your agent.",
|
|
42
|
+
offline: "Lost connection to WhiteRoom. Trying again for up to 5 minutes.",
|
|
43
|
+
deadline: "Your agent didn't finish this practice task in time, so it was stopped.",
|
|
44
|
+
pressAgain: "Press Ctrl+C again to end the practice test. Finished practice tasks are kept.",
|
|
45
|
+
badCode: "That isn't a run code. Copy it again from the dashboard (it starts with wrrt_).",
|
|
46
|
+
needCode: "Paste the run code from the dashboard (it won't show): ",
|
|
47
|
+
needKey: "Paste the model provider key your agent uses (it won't show): ",
|
|
48
|
+
bothKeys: (vars) => `${vars.length === 2 ? `Both ${vars[0]} and ${vars[1]} are` : `${list(vars)} are all`} set. Add --key-env with the one your agent uses (for example --key-env ${vars[0]}), then run the command again. Nothing was sent.`,
|
|
49
|
+
noKey: (keyEnv) => `Set the key your agent uses (${keyEnv ?? list(KEY_VARS, "or")}), then run the command again.`,
|
|
50
|
+
badKeyEnv: (v) => `--key-env must name a provider key variable (${list(KEY_VARS, "or")}), not ${v}.`,
|
|
51
|
+
shortTermKey: CATALOG.keyRefusals.short_term_key,
|
|
52
|
+
node: (v) => `Needs Node 20 or later (you have ${v}).`,
|
|
53
|
+
windows: "Practice tests run on macOS and Linux for now. Run the command there, or in WSL.",
|
|
54
|
+
badUrl: "--url must be a WhiteRoom address (https://….whiteroom.tech) or http://localhost for local testing.",
|
|
55
|
+
adapterMissing: (cmd, why) => `Couldn't start the adapter \`${cmd}\`: ${why}. Nothing was sent or charged, and your run code still works.`,
|
|
56
|
+
keyMismatch: (v, left, fleet = null) => `This key isn't the one your agent uses with ${fleet ?? "this fleet"}. Nothing was charged, and your run code still works (${plural(left, "more try", "more tries")}). Set ${v} to the right key, then run the same command again.`,
|
|
57
|
+
/** §17: a run on a Fleet key provider key names that key as Fleet key does; a new test is the way to pick another. */
|
|
58
|
+
providerKeyMismatch: (from, key, provider, hint) => `${from ? `The key in ${from}` : "The key you pasted"} (ends ${key.slice(-4)}) isn't the ${provider} key saved on Fleet key${hint ? ` (key ending ${hint})` : ""}. Nothing was charged. If you've changed keys, add the new one on Fleet key, then start a new test in the dashboard and pick that key.`,
|
|
59
|
+
lastTry: `That was the last try, so this run code no longer works. Nothing was charged. ${button("Get a new run code")}, then set the right key and run the command again.`,
|
|
60
|
+
codeInvalid: `This run code doesn't work any more: it expired or was already used. ${button("Get a new run code")}, then run the command again.`,
|
|
61
|
+
busy: "Practice tests are busy right now. Try again in a few minutes. Your run code still works.",
|
|
62
|
+
unavailable: "Practice tests aren't available yet. Try again later.",
|
|
63
|
+
unreachable: "Couldn't reach WhiteRoom. Check your connection, then run the command again.",
|
|
64
|
+
cliOutdated: "WhiteRoom sent something this CLI doesn't understand. Update the CLI, then run the test again.",
|
|
65
|
+
redirected: "WhiteRoom's server tried to send the CLI to another address, so it stopped there. Nothing was sent anywhere else. Try again in a few minutes.",
|
|
66
|
+
refused: `WhiteRoom refused the CLI's request. ${button("Start test")} to run it again.`,
|
|
67
|
+
otherHost: "WhiteRoom sent an address on another host, so the CLI stopped before sending your key there.",
|
|
68
|
+
engineUpdate: (kept) => `WhiteRoom was updated during your practice test, so it stopped. ${kept ? `${plural(kept, "finished practice task")} kept. ` : ""}${button("Start test")} to run it again.`,
|
|
69
|
+
endedByOwner: `The practice test was ended from the dashboard. Finished practice tasks were kept. ${button("Start test")} to test again.`,
|
|
70
|
+
endedBeforeStart: `The practice test was ended from the dashboard before it started. ${button("Start test")} to test again.`,
|
|
71
|
+
expired: `This practice test has ended: it lost contact with this computer or ran out of time. ${button("Start test")} to run it again.`,
|
|
72
|
+
callCap: `Stopped at this test's model-call limit after WhiteRoom had to rerun a task (not your agent). ${button("Start test")} to run it again.`,
|
|
73
|
+
noModelCalls: `Stopped: your agent didn't make any model calls through WhiteRoom in 2 practice tasks in a row. Check your adapter sends its calls to the base_url the CLI gives it. ${button("Start test")} to run it again.`,
|
|
74
|
+
engineError: `WhiteRoom couldn't finish working out the result (not your agent). ${button("Start test")} to run a new test.`,
|
|
75
|
+
finished: (verdict) => {
|
|
76
|
+
const label = typeof verdict === "string" && Object.hasOwn(CATALOG.verdictLabels, verdict) ? `: ${CATALOG.verdictLabels[verdict]}` : "";
|
|
77
|
+
return `The practice test finished${label}. See the results in the dashboard (${DASHBOARD}), under Past tests.`;
|
|
78
|
+
},
|
|
79
|
+
sessionGone: `WhiteRoom no longer knows this practice test. ${button("Start test")} to run it again.`,
|
|
80
|
+
cancelled: `Practice test ended. Finished practice tasks were kept. ${button("Start test")} to run it again.`,
|
|
81
|
+
adapterCrashed: (code, tail) => [
|
|
82
|
+
`Your adapter stopped before it finished the practice task (${code}).`,
|
|
83
|
+
...(tail.length ? ["Its last lines:", ...tail.map((l) => ` ${l}`)] : []),
|
|
84
|
+
"Run the adapter on its own to see why.",
|
|
85
|
+
],
|
|
86
|
+
adapterBroke: (why) => `Your adapter ${why}, so it was stopped.`,
|
|
87
|
+
keptResults: `The test ended early. The practice tasks your agent finished are in the results: see the dashboard (${DASHBOARD}), under Past tests.`,
|
|
88
|
+
keptUnconfirmed: `The test ended early. WhiteRoom didn't confirm it kept the results: check Past tests in the dashboard (${DASHBOARD}).`,
|
|
89
|
+
tooManyTools: "made more test-tool calls than a practice task allows",
|
|
90
|
+
badTool: "sent a tool call WhiteRoom couldn't accept",
|
|
91
|
+
noTerminal: "There's no terminal to paste the run code into. Pipe it in with --code-stdin instead.",
|
|
92
|
+
stoppedEarly: "Stopped before the practice test started.",
|
|
93
|
+
};
|
|
94
|
+
const STATUS = CATALOG.statusLabels;
|
|
95
|
+
/** The results screen in the terminal, and its exit code: 0 Passed, 1 Didn't pass, 3 Didn't finish. */
|
|
96
|
+
export function results(r) {
|
|
97
|
+
const required = r.checks.filter((c) => c.required);
|
|
98
|
+
const runs = plural(r.runs, "run");
|
|
99
|
+
const failed = required.filter((c) => c.status === "failed");
|
|
100
|
+
const open = required.filter((c) => c.status !== "passed");
|
|
101
|
+
const scope = r.scope && [...new Set(required.map((c) => c.scenario).filter(Boolean))].join(", ");
|
|
102
|
+
const lines = [""];
|
|
103
|
+
let code;
|
|
104
|
+
if (r.verdict === "passed") {
|
|
105
|
+
code = 0;
|
|
106
|
+
lines.push(`${CATALOG.verdictLabels.passed} · ${scope ? `${scope} only` : `all ${plural(required.length, "check")}`} · ${runs}`);
|
|
107
|
+
}
|
|
108
|
+
else if (r.verdict === "not_passed") {
|
|
109
|
+
code = 1;
|
|
110
|
+
lines.push(`${CATALOG.verdictLabels.not_passed} · ${plural(failed.length, "check")} failed`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
code = 3;
|
|
114
|
+
const ours = open.length > 0 && open.every((c) => c.testProblem);
|
|
115
|
+
lines.push(`${CATALOG.verdictLabels.incomplete} · ${open.length ? `${plural(open.length, "check")} couldn't run` : "no checks could run"}${ours ? " (not your agent)" : ""}`);
|
|
116
|
+
}
|
|
117
|
+
if (r.sample)
|
|
118
|
+
lines.push("This was WhiteRoom's sample agent: it checks your controls and this setup, not your own agent.");
|
|
119
|
+
else if (r.runs === 1 && code === 0)
|
|
120
|
+
lines.push("One run shows your agent can pass. Run it 3 times to see that it passes every time.");
|
|
121
|
+
lines.push("");
|
|
122
|
+
for (const c of r.checks) {
|
|
123
|
+
const name = c.mode ? `${c.label} · ${c.mode}` : c.label;
|
|
124
|
+
// A control row says what happened; a row that wasn't checked says why instead of a status.
|
|
125
|
+
if (c.status === "unsupported" && c.summary)
|
|
126
|
+
lines.push(` ${name}: ${c.summary}`);
|
|
127
|
+
else
|
|
128
|
+
lines.push(` ${STATUS[c.status]}: ${name}${c.id.startsWith("ctl") && c.summary ? ` (${c.summary})` : ""}`);
|
|
129
|
+
}
|
|
130
|
+
const fixes = (code === 1 ? failed : code === 3 ? open : []).filter((c) => c.fix);
|
|
131
|
+
if (fixes.length) {
|
|
132
|
+
lines.push("", code === 3 ? "What stopped the test" : "What to fix");
|
|
133
|
+
for (const c of fixes)
|
|
134
|
+
lines.push(` ${c.label}: ${c.fix}`);
|
|
135
|
+
}
|
|
136
|
+
const next = code === 1 ? "Rerun failed checks" : code === 3 ? "Rerun checks that didn't run" : !r.sample && r.runs === 1 ? "Run it 3 times" : "Past tests";
|
|
137
|
+
lines.push("", CATALOG.disclaimer, next === "Past tests" ? `See the full result in the dashboard (${DASHBOARD}), under Past tests.` : `${button(next)}.`);
|
|
138
|
+
return { code, lines, result: true };
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=text.js.map
|