@attest-ai/core 1.0.0-rc.3 → 1.0.0-rc.5
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/cjs/index.cjs +356 -32
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +146 -5
- package/dist/cjs/proto/index.cjs.map +1 -1
- package/dist/cjs/proto/index.d.cts +14 -1
- package/dist/esm/cli.d.ts +3 -1
- package/dist/esm/cli.js +238 -31
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index.d.ts +146 -5
- package/dist/esm/index.js +346 -32
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/proto/index.d.ts +14 -1
- package/dist/esm/proto/index.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -219,8 +219,8 @@ function extractId(response) {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
// src/version.ts
|
|
222
|
-
var VERSION = "1.0.0-rc.
|
|
223
|
-
var ENGINE_VERSION = "1.0.0-rc.
|
|
222
|
+
var VERSION = "1.0.0-rc.5";
|
|
223
|
+
var ENGINE_VERSION = "1.0.0-rc.5";
|
|
224
224
|
|
|
225
225
|
// src/engine-manager.ts
|
|
226
226
|
import { spawn, spawnSync } from "child_process";
|
|
@@ -288,7 +288,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
288
288
|
new Error(`Too many redirects (>${MAX_REDIRECTS}) following ${url}`)
|
|
289
289
|
);
|
|
290
290
|
}
|
|
291
|
-
return new Promise((
|
|
291
|
+
return new Promise((resolve5, reject) => {
|
|
292
292
|
const client = url.startsWith("https:") ? https : http;
|
|
293
293
|
client.get(url, (res) => {
|
|
294
294
|
const status = res.statusCode ?? 0;
|
|
@@ -299,7 +299,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
299
299
|
return;
|
|
300
300
|
}
|
|
301
301
|
res.resume();
|
|
302
|
-
|
|
302
|
+
resolve5(httpsGet(location, redirectCount + 1));
|
|
303
303
|
return;
|
|
304
304
|
}
|
|
305
305
|
if (status < 200 || status >= 300) {
|
|
@@ -309,7 +309,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
309
309
|
}
|
|
310
310
|
const chunks = [];
|
|
311
311
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
312
|
-
res.on("end", () =>
|
|
312
|
+
res.on("end", () => resolve5(Buffer.concat(chunks)));
|
|
313
313
|
res.on("error", reject);
|
|
314
314
|
}).on("error", reject);
|
|
315
315
|
});
|
|
@@ -536,11 +536,11 @@ var EngineManager = class {
|
|
|
536
536
|
}
|
|
537
537
|
this.requestId += 1;
|
|
538
538
|
const requestStr = encodeRequest(this.requestId, method, params);
|
|
539
|
-
return new Promise((
|
|
539
|
+
return new Promise((resolve5, reject) => {
|
|
540
540
|
const onLine = (line) => {
|
|
541
541
|
try {
|
|
542
542
|
const response = decodeResponse(line);
|
|
543
|
-
|
|
543
|
+
resolve5(extractResult(response));
|
|
544
544
|
} catch (err) {
|
|
545
545
|
reject(err);
|
|
546
546
|
}
|
|
@@ -907,12 +907,12 @@ var AttestClient = class {
|
|
|
907
907
|
throw new Error("Engine process not started.");
|
|
908
908
|
}
|
|
909
909
|
let capturedReqId;
|
|
910
|
-
const requestPromise = new Promise((
|
|
910
|
+
const requestPromise = new Promise((resolve5, reject) => {
|
|
911
911
|
this.writeChain = this.writeChain.then(() => {
|
|
912
912
|
this.requestId += 1;
|
|
913
913
|
const reqId = this.requestId;
|
|
914
914
|
capturedReqId = reqId;
|
|
915
|
-
this.pending.set(reqId, { resolve:
|
|
915
|
+
this.pending.set(reqId, { resolve: resolve5, reject });
|
|
916
916
|
const requestStr = encodeRequest(reqId, method, params);
|
|
917
917
|
cp.stdin.write(requestStr, (err) => {
|
|
918
918
|
if (err) {
|
|
@@ -937,7 +937,8 @@ var AttestClient = class {
|
|
|
937
937
|
const params = {
|
|
938
938
|
trace,
|
|
939
939
|
assertions: [...assertions],
|
|
940
|
-
...options2?.budget !== void 0 ? { budget: options2.budget } : {}
|
|
940
|
+
...options2?.budget !== void 0 ? { budget: options2.budget } : {},
|
|
941
|
+
...options2?.context !== void 0 ? { context: options2.context } : {}
|
|
941
942
|
};
|
|
942
943
|
const raw = await this.sendRequest(
|
|
943
944
|
"evaluate_batch",
|
|
@@ -1026,6 +1027,26 @@ function requireSimulationCapability(init) {
|
|
|
1026
1027
|
|
|
1027
1028
|
// src/trace.ts
|
|
1028
1029
|
import { randomUUID } from "crypto";
|
|
1030
|
+
|
|
1031
|
+
// src/simulation/context.ts
|
|
1032
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
1033
|
+
var activeMockRegistry = new AsyncLocalStorage();
|
|
1034
|
+
function createRng(seed) {
|
|
1035
|
+
if (seed === void 0) return Math.random;
|
|
1036
|
+
let state = seed >>> 0;
|
|
1037
|
+
return () => {
|
|
1038
|
+
state = state + 1831565813 >>> 0;
|
|
1039
|
+
let t = state;
|
|
1040
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
1041
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
1042
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function sleep(ms) {
|
|
1046
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// src/trace.ts
|
|
1029
1050
|
var TraceBuilder = class {
|
|
1030
1051
|
traceId;
|
|
1031
1052
|
agentId;
|
|
@@ -1057,11 +1078,22 @@ var TraceBuilder = class {
|
|
|
1057
1078
|
return this;
|
|
1058
1079
|
}
|
|
1059
1080
|
addToolCall(name, options2) {
|
|
1081
|
+
const mock = activeMockRegistry.getStore()?.get(name);
|
|
1082
|
+
let result = options2?.result;
|
|
1083
|
+
if (mock !== void 0) {
|
|
1084
|
+
const mocked = mock(options2?.args ?? {});
|
|
1085
|
+
if (typeof mocked?.then === "function") {
|
|
1086
|
+
throw new Error(`mock for tool '${name}' returned a Promise; mock tools must be synchronous`);
|
|
1087
|
+
}
|
|
1088
|
+
if (typeof mocked === "object" && mocked !== null && !Array.isArray(mocked)) {
|
|
1089
|
+
result = mocked;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1060
1092
|
this.steps.push({
|
|
1061
1093
|
type: STEP_TOOL_CALL,
|
|
1062
1094
|
name,
|
|
1063
1095
|
args: options2?.args,
|
|
1064
|
-
result
|
|
1096
|
+
result,
|
|
1065
1097
|
metadata: options2?.metadata
|
|
1066
1098
|
});
|
|
1067
1099
|
return this;
|
|
@@ -2716,18 +2748,18 @@ function serve(engine, args) {
|
|
|
2716
2748
|
let stdout = "";
|
|
2717
2749
|
let stderr = "";
|
|
2718
2750
|
let resolveReady = () => void 0;
|
|
2719
|
-
const ready = new Promise((
|
|
2720
|
-
resolveReady =
|
|
2751
|
+
const ready = new Promise((resolve5) => {
|
|
2752
|
+
resolveReady = resolve5;
|
|
2721
2753
|
});
|
|
2722
|
-
const exited = new Promise((
|
|
2754
|
+
const exited = new Promise((resolve5) => {
|
|
2723
2755
|
child.on("close", (code, signal) => {
|
|
2724
2756
|
resolveReady(null);
|
|
2725
|
-
|
|
2757
|
+
resolve5({ code, signal });
|
|
2726
2758
|
});
|
|
2727
2759
|
child.on("error", (err) => {
|
|
2728
2760
|
stderr += err.message;
|
|
2729
2761
|
resolveReady(null);
|
|
2730
|
-
|
|
2762
|
+
resolve5({ code: null, signal: null });
|
|
2731
2763
|
});
|
|
2732
2764
|
});
|
|
2733
2765
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -2984,18 +3016,18 @@ async function start(name, mode, opts) {
|
|
|
2984
3016
|
let stdout = "";
|
|
2985
3017
|
let stderr = "";
|
|
2986
3018
|
let resolveReady = () => void 0;
|
|
2987
|
-
const ready = new Promise((
|
|
2988
|
-
resolveReady =
|
|
3019
|
+
const ready = new Promise((resolve5) => {
|
|
3020
|
+
resolveReady = resolve5;
|
|
2989
3021
|
});
|
|
2990
|
-
const exited = new Promise((
|
|
3022
|
+
const exited = new Promise((resolve5) => {
|
|
2991
3023
|
child.on("close", (code) => {
|
|
2992
3024
|
resolveReady(null);
|
|
2993
|
-
|
|
3025
|
+
resolve5(code);
|
|
2994
3026
|
});
|
|
2995
3027
|
child.on("error", (err) => {
|
|
2996
3028
|
stderr += err.message;
|
|
2997
3029
|
resolveReady(null);
|
|
2998
|
-
|
|
3030
|
+
resolve5(null);
|
|
2999
3031
|
});
|
|
3000
3032
|
});
|
|
3001
3033
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3120,18 +3152,18 @@ async function start2(file, opts) {
|
|
|
3120
3152
|
let stdout = "";
|
|
3121
3153
|
let stderr = "";
|
|
3122
3154
|
let resolveReady = () => void 0;
|
|
3123
|
-
const ready = new Promise((
|
|
3124
|
-
resolveReady =
|
|
3155
|
+
const ready = new Promise((resolve5) => {
|
|
3156
|
+
resolveReady = resolve5;
|
|
3125
3157
|
});
|
|
3126
|
-
const exited = new Promise((
|
|
3158
|
+
const exited = new Promise((resolve5) => {
|
|
3127
3159
|
child.on("close", (code) => {
|
|
3128
3160
|
resolveReady(null);
|
|
3129
|
-
|
|
3161
|
+
resolve5(code);
|
|
3130
3162
|
});
|
|
3131
3163
|
child.on("error", (err) => {
|
|
3132
3164
|
stderr += err.message;
|
|
3133
3165
|
resolveReady(null);
|
|
3134
|
-
|
|
3166
|
+
resolve5(null);
|
|
3135
3167
|
});
|
|
3136
3168
|
});
|
|
3137
3169
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3212,8 +3244,8 @@ function parseFinal(out) {
|
|
|
3212
3244
|
}
|
|
3213
3245
|
|
|
3214
3246
|
// src/context.ts
|
|
3215
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
3216
|
-
var activeBuilder = new
|
|
3247
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
3248
|
+
var activeBuilder = new AsyncLocalStorage2();
|
|
3217
3249
|
|
|
3218
3250
|
// src/agent.ts
|
|
3219
3251
|
var Agent = class {
|
|
@@ -3300,6 +3332,9 @@ function tier(level) {
|
|
|
3300
3332
|
}
|
|
3301
3333
|
|
|
3302
3334
|
// src/plugins.ts
|
|
3335
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
3336
|
+
import { dirname as dirname2, join as join4, relative as relative2, resolve as resolve4, isAbsolute, sep as sep2 } from "path";
|
|
3337
|
+
import { pathToFileURL } from "url";
|
|
3303
3338
|
var PluginRegistry = class {
|
|
3304
3339
|
plugins = /* @__PURE__ */ new Map();
|
|
3305
3340
|
register(name, plugin) {
|
|
@@ -3330,10 +3365,19 @@ var PluginRegistry = class {
|
|
|
3330
3365
|
};
|
|
3331
3366
|
async function executePluginAssertion(plugin, trace, spec, client, traceId2, assertionId2, timeoutMs = 3e4) {
|
|
3332
3367
|
const resultPromise = Promise.resolve(plugin.execute(trace, spec));
|
|
3368
|
+
let timer;
|
|
3333
3369
|
const timeoutPromise = new Promise((_, reject) => {
|
|
3334
|
-
|
|
3370
|
+
timer = setTimeout(
|
|
3371
|
+
() => reject(new Error(`Plugin '${plugin.name}' timed out after ${timeoutMs}ms`)),
|
|
3372
|
+
timeoutMs
|
|
3373
|
+
);
|
|
3335
3374
|
});
|
|
3336
|
-
|
|
3375
|
+
let result;
|
|
3376
|
+
try {
|
|
3377
|
+
result = await Promise.race([resultPromise, timeoutPromise]);
|
|
3378
|
+
} finally {
|
|
3379
|
+
clearTimeout(timer);
|
|
3380
|
+
}
|
|
3337
3381
|
await client.submitPluginResult(
|
|
3338
3382
|
traceId2,
|
|
3339
3383
|
plugin.name,
|
|
@@ -3344,6 +3388,100 @@ async function executePluginAssertion(plugin, trace, spec, client, traceId2, ass
|
|
|
3344
3388
|
);
|
|
3345
3389
|
return result;
|
|
3346
3390
|
}
|
|
3391
|
+
function registerPlugin(registry, name, plugin) {
|
|
3392
|
+
registry.register(name, plugin);
|
|
3393
|
+
}
|
|
3394
|
+
function isAttestPlugin(value) {
|
|
3395
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3396
|
+
const v = value;
|
|
3397
|
+
return typeof v["name"] === "string" && typeof v["pluginType"] === "string" && typeof v["execute"] === "function";
|
|
3398
|
+
}
|
|
3399
|
+
function readJson(path5) {
|
|
3400
|
+
return JSON.parse(readFileSync2(path5, "utf8"));
|
|
3401
|
+
}
|
|
3402
|
+
function findInstalledPackage(projectRoot, name) {
|
|
3403
|
+
let dir = resolve4(projectRoot);
|
|
3404
|
+
for (; ; ) {
|
|
3405
|
+
const candidate = join4(dir, "node_modules", name);
|
|
3406
|
+
if (existsSync2(join4(candidate, "package.json"))) return candidate;
|
|
3407
|
+
const parent = dirname2(dir);
|
|
3408
|
+
if (parent === dir) return void 0;
|
|
3409
|
+
dir = parent;
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
function declaredPluginPaths(pkgJson, dep) {
|
|
3413
|
+
const attest = pkgJson["attest"];
|
|
3414
|
+
if (attest === void 0) return [];
|
|
3415
|
+
const plugins = typeof attest === "object" && attest !== null ? attest["plugins"] : void 0;
|
|
3416
|
+
if (plugins === void 0) return [];
|
|
3417
|
+
if (!Array.isArray(plugins) || !plugins.every((p) => typeof p === "string")) {
|
|
3418
|
+
throw new Error(`'${dep}': "attest.plugins" must be an array of strings`);
|
|
3419
|
+
}
|
|
3420
|
+
return plugins;
|
|
3421
|
+
}
|
|
3422
|
+
function pluginsOf(mod) {
|
|
3423
|
+
const def = mod["default"];
|
|
3424
|
+
const nested = typeof def === "object" && def !== null ? def : void 0;
|
|
3425
|
+
for (const candidate of [def, nested?.["default"]]) {
|
|
3426
|
+
if (isAttestPlugin(candidate)) return [candidate];
|
|
3427
|
+
}
|
|
3428
|
+
return [.../* @__PURE__ */ new Set([...Object.values(mod), ...Object.values(nested ?? {})])].filter(isAttestPlugin);
|
|
3429
|
+
}
|
|
3430
|
+
function isInside(root, file) {
|
|
3431
|
+
const within = (r, f) => {
|
|
3432
|
+
const rel = relative2(r, f);
|
|
3433
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute(rel);
|
|
3434
|
+
};
|
|
3435
|
+
return within(root, file) && within(realpathSync(root), realpathSync(file));
|
|
3436
|
+
}
|
|
3437
|
+
async function loadPackagePlugins(registry, projectRoot = process.cwd()) {
|
|
3438
|
+
let project;
|
|
3439
|
+
try {
|
|
3440
|
+
project = readJson(join4(projectRoot, "package.json"));
|
|
3441
|
+
} catch (err) {
|
|
3442
|
+
console.error(`attest: cannot read ${join4(projectRoot, "package.json")}; no plugins loaded:`, err);
|
|
3443
|
+
return 0;
|
|
3444
|
+
}
|
|
3445
|
+
const depNames = /* @__PURE__ */ new Set();
|
|
3446
|
+
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
3447
|
+
const deps = project[field];
|
|
3448
|
+
if (typeof deps === "object" && deps !== null) {
|
|
3449
|
+
for (const dep of Object.keys(deps)) depNames.add(dep);
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
let loaded = 0;
|
|
3453
|
+
for (const dep of depNames) {
|
|
3454
|
+
const pkgDir = findInstalledPackage(projectRoot, dep);
|
|
3455
|
+
if (pkgDir === void 0) continue;
|
|
3456
|
+
let entries;
|
|
3457
|
+
try {
|
|
3458
|
+
entries = declaredPluginPaths(readJson(join4(pkgDir, "package.json")), dep);
|
|
3459
|
+
} catch (err) {
|
|
3460
|
+
console.error(`attest: failed to read plugins declared by '${dep}':`, err);
|
|
3461
|
+
continue;
|
|
3462
|
+
}
|
|
3463
|
+
for (const entry of entries) {
|
|
3464
|
+
try {
|
|
3465
|
+
const file = resolve4(pkgDir, entry);
|
|
3466
|
+
if (!isInside(pkgDir, file)) {
|
|
3467
|
+
throw new Error(`plugin path '${entry}' escapes package '${dep}'`);
|
|
3468
|
+
}
|
|
3469
|
+
const mod = await import(pathToFileURL(file).href);
|
|
3470
|
+
const plugins = pluginsOf(mod);
|
|
3471
|
+
if (plugins.length === 0) {
|
|
3472
|
+
throw new Error(`'${entry}' exports no plugin (name, pluginType, execute)`);
|
|
3473
|
+
}
|
|
3474
|
+
for (const plugin of plugins) {
|
|
3475
|
+
registry.register(plugin.name, plugin);
|
|
3476
|
+
loaded++;
|
|
3477
|
+
}
|
|
3478
|
+
} catch (err) {
|
|
3479
|
+
console.error(`attest: failed to load plugin '${entry}' from '${dep}':`, err);
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
return loaded;
|
|
3484
|
+
}
|
|
3347
3485
|
|
|
3348
3486
|
// src/continuous.ts
|
|
3349
3487
|
import { EventEmitter } from "events";
|
|
@@ -3723,6 +3861,172 @@ the assistant's response meets your needs. Keep responses concise (1-3 sentences
|
|
|
3723
3861
|
max_tokens: 200
|
|
3724
3862
|
};
|
|
3725
3863
|
|
|
3864
|
+
// src/simulation/mock-tools.ts
|
|
3865
|
+
var MockToolRegistry = class {
|
|
3866
|
+
tools = /* @__PURE__ */ new Map();
|
|
3867
|
+
register(name, fn) {
|
|
3868
|
+
this.tools.set(name, fn);
|
|
3869
|
+
}
|
|
3870
|
+
/**
|
|
3871
|
+
* Run `fn` with the registered mocks active. `TraceBuilder.addToolCall`
|
|
3872
|
+
* returns the mock's result for a registered tool name. Scopes are
|
|
3873
|
+
* isolated per async call chain (AsyncLocalStorage).
|
|
3874
|
+
*/
|
|
3875
|
+
run(fn) {
|
|
3876
|
+
return activeMockRegistry.run(new Map(this.tools), fn);
|
|
3877
|
+
}
|
|
3878
|
+
};
|
|
3879
|
+
function mockTool(name, fn) {
|
|
3880
|
+
const wrapper = ((args) => fn(args));
|
|
3881
|
+
return Object.assign(wrapper, { mockToolName: name });
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
// src/simulation/fault-inject.ts
|
|
3885
|
+
function faultInject(options2, fn) {
|
|
3886
|
+
const errorRate = options2.errorRate ?? 0;
|
|
3887
|
+
const latencyJitterMs = options2.latencyJitterMs ?? 0;
|
|
3888
|
+
const rng = createRng(options2.seed);
|
|
3889
|
+
return async (...args) => {
|
|
3890
|
+
if (errorRate > 0 && rng() < errorRate) {
|
|
3891
|
+
throw new Error(`Injected fault in ${fn.name || "anonymous"}`);
|
|
3892
|
+
}
|
|
3893
|
+
if (latencyJitterMs > 0) {
|
|
3894
|
+
await sleep(rng() * latencyJitterMs);
|
|
3895
|
+
}
|
|
3896
|
+
return fn(...args);
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
// src/simulation/verdict.ts
|
|
3901
|
+
function aggregateOutcomes(outcomes) {
|
|
3902
|
+
if (outcomes.includes(OUTCOME_FAIL)) return OUTCOME_FAIL;
|
|
3903
|
+
if (outcomes.includes(OUTCOME_SKIP)) return OUTCOME_SKIP;
|
|
3904
|
+
return OUTCOME_PASS;
|
|
3905
|
+
}
|
|
3906
|
+
function sumNotEvaluatedReasons(results) {
|
|
3907
|
+
const counts = {};
|
|
3908
|
+
for (const r of results) {
|
|
3909
|
+
for (const [reason, n] of Object.entries(r.notEvaluatedReasons)) {
|
|
3910
|
+
counts[reason] = (counts[reason] ?? 0) + n;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
return counts;
|
|
3914
|
+
}
|
|
3915
|
+
function formatSkipReason(counts) {
|
|
3916
|
+
const parts = Object.entries(counts).map(([reason, n]) => n === 1 ? reason : `${reason} \xD7${n}`);
|
|
3917
|
+
return `not evaluated: ${parts.join(", ")}`;
|
|
3918
|
+
}
|
|
3919
|
+
function renderVerdictDiagnostics(verdict, color = false) {
|
|
3920
|
+
return verdict instanceof AgentResult ? renderDiagnostics(verdict, { color }) : verdict.renderDiagnostics?.({ color }) ?? "";
|
|
3921
|
+
}
|
|
3922
|
+
function verdictsDiagnostics(results, label, color = false) {
|
|
3923
|
+
const blocks = results.flatMap(
|
|
3924
|
+
(r, i) => r.outcome() === OUTCOME_PASS ? [] : [`${label} ${i + 1}/${results.length}:${renderVerdictDiagnostics(r, color)}`]
|
|
3925
|
+
);
|
|
3926
|
+
return blocks.length > 0 ? blocks.join("\n") : ` (no failing ${label}s)
|
|
3927
|
+
`;
|
|
3928
|
+
}
|
|
3929
|
+
|
|
3930
|
+
// src/simulation/repeat.ts
|
|
3931
|
+
var RepeatResult = class {
|
|
3932
|
+
results;
|
|
3933
|
+
constructor(results = []) {
|
|
3934
|
+
this.results = results;
|
|
3935
|
+
}
|
|
3936
|
+
get count() {
|
|
3937
|
+
return this.results.length;
|
|
3938
|
+
}
|
|
3939
|
+
/** Trials skipped because an assertion was not evaluated. */
|
|
3940
|
+
get notEvaluated() {
|
|
3941
|
+
return this.results.filter((r) => r.outcome() === OUTCOME_SKIP).length;
|
|
3942
|
+
}
|
|
3943
|
+
/** Passing / evaluated trials; null when no trial was evaluated. */
|
|
3944
|
+
get passRate() {
|
|
3945
|
+
const evaluated = this.count - this.notEvaluated;
|
|
3946
|
+
if (evaluated === 0) return null;
|
|
3947
|
+
return this.results.filter((r) => r.outcome() === OUTCOME_PASS).length / evaluated;
|
|
3948
|
+
}
|
|
3949
|
+
/** True when every trial's outcome is pass (soft failures included). */
|
|
3950
|
+
get allPassed() {
|
|
3951
|
+
return this.results.every((r) => r.outcome() === OUTCOME_PASS);
|
|
3952
|
+
}
|
|
3953
|
+
outcome(options2) {
|
|
3954
|
+
return aggregateOutcomes(this.results.map((r) => r.outcome(options2)));
|
|
3955
|
+
}
|
|
3956
|
+
get notEvaluatedReasons() {
|
|
3957
|
+
return sumNotEvaluatedReasons(this.results);
|
|
3958
|
+
}
|
|
3959
|
+
skipReason() {
|
|
3960
|
+
return formatSkipReason(this.notEvaluatedReasons);
|
|
3961
|
+
}
|
|
3962
|
+
renderDiagnostics(options2) {
|
|
3963
|
+
return verdictsDiagnostics(this.results, "trial", options2?.color);
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
function repeat(n, fn) {
|
|
3967
|
+
return async (...args) => {
|
|
3968
|
+
const results = [];
|
|
3969
|
+
for (let i = 0; i < n; i++) {
|
|
3970
|
+
results.push(await fn(...args));
|
|
3971
|
+
}
|
|
3972
|
+
return new RepeatResult(results);
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
// src/simulation/scenario.ts
|
|
3977
|
+
var ScenarioResult = class {
|
|
3978
|
+
config;
|
|
3979
|
+
results;
|
|
3980
|
+
constructor(config2, results = []) {
|
|
3981
|
+
this.config = config2;
|
|
3982
|
+
this.results = results;
|
|
3983
|
+
}
|
|
3984
|
+
/** True when the aggregate `outcome()` is pass. */
|
|
3985
|
+
get passed() {
|
|
3986
|
+
return this.outcome() === OUTCOME_PASS;
|
|
3987
|
+
}
|
|
3988
|
+
outcome(options2) {
|
|
3989
|
+
return aggregateOutcomes(this.results.map((r) => r.outcome(options2)));
|
|
3990
|
+
}
|
|
3991
|
+
get notEvaluatedReasons() {
|
|
3992
|
+
return sumNotEvaluatedReasons(this.results);
|
|
3993
|
+
}
|
|
3994
|
+
skipReason() {
|
|
3995
|
+
return formatSkipReason(this.notEvaluatedReasons);
|
|
3996
|
+
}
|
|
3997
|
+
renderDiagnostics(options2) {
|
|
3998
|
+
return verdictsDiagnostics(this.results, "turn", options2?.color);
|
|
3999
|
+
}
|
|
4000
|
+
};
|
|
4001
|
+
function scenario(options2, fn) {
|
|
4002
|
+
const config2 = {
|
|
4003
|
+
persona: options2.persona,
|
|
4004
|
+
mockTools: { ...options2.mockTools },
|
|
4005
|
+
faultErrorRate: options2.faultErrorRate ?? 0,
|
|
4006
|
+
faultLatencyJitterMs: options2.faultLatencyJitterMs ?? 0,
|
|
4007
|
+
maxTurns: options2.maxTurns ?? 1,
|
|
4008
|
+
seed: options2.seed
|
|
4009
|
+
};
|
|
4010
|
+
return async (...args) => {
|
|
4011
|
+
const run = async () => {
|
|
4012
|
+
const results = [];
|
|
4013
|
+
const rng = createRng(config2.seed);
|
|
4014
|
+
for (let turn = 0; turn < config2.maxTurns; turn++) {
|
|
4015
|
+
if (config2.faultErrorRate > 0 && rng() < config2.faultErrorRate) {
|
|
4016
|
+
throw new Error(`Injected fault at turn ${turn}`);
|
|
4017
|
+
}
|
|
4018
|
+
if (config2.faultLatencyJitterMs > 0) {
|
|
4019
|
+
await sleep(rng() * config2.faultLatencyJitterMs);
|
|
4020
|
+
}
|
|
4021
|
+
results.push(await fn({ persona: config2.persona, turn }, ...args));
|
|
4022
|
+
}
|
|
4023
|
+
return new ScenarioResult(config2, results);
|
|
4024
|
+
};
|
|
4025
|
+
const mocks = new Map(Object.entries(config2.mockTools));
|
|
4026
|
+
return mocks.size > 0 ? activeMockRegistry.run(mocks, run) : run();
|
|
4027
|
+
};
|
|
4028
|
+
}
|
|
4029
|
+
|
|
3726
4030
|
// src/adapters/manual.ts
|
|
3727
4031
|
var ManualAdapter = class {
|
|
3728
4032
|
agentId;
|
|
@@ -3934,7 +4238,7 @@ var OTelAdapter = class _OTelAdapter {
|
|
|
3934
4238
|
}
|
|
3935
4239
|
};
|
|
3936
4240
|
function runConvert(engine, args, payload) {
|
|
3937
|
-
return new Promise((
|
|
4241
|
+
return new Promise((resolve5, reject) => {
|
|
3938
4242
|
const child = spawn5(engine, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
3939
4243
|
let timer;
|
|
3940
4244
|
child.on("error", (e) => {
|
|
@@ -3987,7 +4291,7 @@ function runConvert(engine, args, payload) {
|
|
|
3987
4291
|
);
|
|
3988
4292
|
return;
|
|
3989
4293
|
}
|
|
3990
|
-
|
|
4294
|
+
resolve5(Buffer.concat(out).toString("utf8"));
|
|
3991
4295
|
});
|
|
3992
4296
|
stdin.end(payload);
|
|
3993
4297
|
});
|
|
@@ -4154,6 +4458,7 @@ export {
|
|
|
4154
4458
|
GeminiAdapter,
|
|
4155
4459
|
LangChainAdapter,
|
|
4156
4460
|
ManualAdapter,
|
|
4461
|
+
MockToolRegistry,
|
|
4157
4462
|
OTLPConversionError,
|
|
4158
4463
|
OTelAdapter,
|
|
4159
4464
|
OUTCOME_FAIL,
|
|
@@ -4173,6 +4478,7 @@ export {
|
|
|
4173
4478
|
REASON_REPLAY_MISS,
|
|
4174
4479
|
REASON_SHORT_CIRCUITED,
|
|
4175
4480
|
REASON_SIMULATION,
|
|
4481
|
+
RepeatResult,
|
|
4176
4482
|
ReplayError,
|
|
4177
4483
|
ReplayMissError,
|
|
4178
4484
|
ReplayNotEvaluatedError,
|
|
@@ -4185,6 +4491,7 @@ export {
|
|
|
4185
4491
|
STEP_RETRIEVAL,
|
|
4186
4492
|
STEP_TOOL_CALL,
|
|
4187
4493
|
Sampler,
|
|
4494
|
+
ScenarioResult,
|
|
4188
4495
|
TIER_1,
|
|
4189
4496
|
TIER_2,
|
|
4190
4497
|
TIER_3,
|
|
@@ -4219,6 +4526,7 @@ export {
|
|
|
4219
4526
|
executePluginAssertion,
|
|
4220
4527
|
extractId,
|
|
4221
4528
|
extractResult,
|
|
4529
|
+
faultInject,
|
|
4222
4530
|
firstMiss,
|
|
4223
4531
|
formatUsage,
|
|
4224
4532
|
getSampleRate,
|
|
@@ -4230,17 +4538,23 @@ export {
|
|
|
4230
4538
|
loadFixture,
|
|
4231
4539
|
loadLabelsCSV,
|
|
4232
4540
|
loadLabelsJSONL,
|
|
4541
|
+
loadPackagePlugins,
|
|
4542
|
+
mockTool,
|
|
4233
4543
|
parseUsage,
|
|
4234
4544
|
previewLine,
|
|
4235
4545
|
promptHash,
|
|
4236
4546
|
record,
|
|
4547
|
+
registerPlugin,
|
|
4237
4548
|
renderAssertionFailure,
|
|
4238
4549
|
renderDiagnostics,
|
|
4239
4550
|
renderSummary,
|
|
4551
|
+
renderVerdictDiagnostics,
|
|
4240
4552
|
renderVitest,
|
|
4553
|
+
repeat,
|
|
4241
4554
|
requireSimulationCapability,
|
|
4242
4555
|
resetConfig,
|
|
4243
4556
|
resolveMode,
|
|
4557
|
+
scenario,
|
|
4244
4558
|
simulationEnvEnabled,
|
|
4245
4559
|
startReplay,
|
|
4246
4560
|
startWorld,
|