@attest-ai/core 1.0.0-rc.4 → 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 +354 -31
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +144 -4
- package/dist/esm/cli.js +10 -2
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index.d.ts +144 -4
- package/dist/esm/index.js +344 -31
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -56,6 +56,7 @@ __export(index_exports, {
|
|
|
56
56
|
GeminiAdapter: () => GeminiAdapter,
|
|
57
57
|
LangChainAdapter: () => LangChainAdapter,
|
|
58
58
|
ManualAdapter: () => ManualAdapter,
|
|
59
|
+
MockToolRegistry: () => MockToolRegistry,
|
|
59
60
|
OTLPConversionError: () => OTLPConversionError,
|
|
60
61
|
OTelAdapter: () => OTelAdapter,
|
|
61
62
|
OUTCOME_FAIL: () => OUTCOME_FAIL,
|
|
@@ -75,6 +76,7 @@ __export(index_exports, {
|
|
|
75
76
|
REASON_REPLAY_MISS: () => REASON_REPLAY_MISS,
|
|
76
77
|
REASON_SHORT_CIRCUITED: () => REASON_SHORT_CIRCUITED,
|
|
77
78
|
REASON_SIMULATION: () => REASON_SIMULATION,
|
|
79
|
+
RepeatResult: () => RepeatResult,
|
|
78
80
|
ReplayError: () => ReplayError,
|
|
79
81
|
ReplayMissError: () => ReplayMissError,
|
|
80
82
|
ReplayNotEvaluatedError: () => ReplayNotEvaluatedError,
|
|
@@ -87,6 +89,7 @@ __export(index_exports, {
|
|
|
87
89
|
STEP_RETRIEVAL: () => STEP_RETRIEVAL,
|
|
88
90
|
STEP_TOOL_CALL: () => STEP_TOOL_CALL,
|
|
89
91
|
Sampler: () => Sampler,
|
|
92
|
+
ScenarioResult: () => ScenarioResult,
|
|
90
93
|
TIER_1: () => TIER_1,
|
|
91
94
|
TIER_2: () => TIER_2,
|
|
92
95
|
TIER_3: () => TIER_3,
|
|
@@ -121,6 +124,7 @@ __export(index_exports, {
|
|
|
121
124
|
executePluginAssertion: () => executePluginAssertion,
|
|
122
125
|
extractId: () => extractId,
|
|
123
126
|
extractResult: () => extractResult,
|
|
127
|
+
faultInject: () => faultInject,
|
|
124
128
|
firstMiss: () => firstMiss,
|
|
125
129
|
formatUsage: () => formatUsage,
|
|
126
130
|
getSampleRate: () => getSampleRate,
|
|
@@ -132,17 +136,23 @@ __export(index_exports, {
|
|
|
132
136
|
loadFixture: () => loadFixture,
|
|
133
137
|
loadLabelsCSV: () => loadLabelsCSV,
|
|
134
138
|
loadLabelsJSONL: () => loadLabelsJSONL,
|
|
139
|
+
loadPackagePlugins: () => loadPackagePlugins,
|
|
140
|
+
mockTool: () => mockTool,
|
|
135
141
|
parseUsage: () => parseUsage,
|
|
136
142
|
previewLine: () => previewLine,
|
|
137
143
|
promptHash: () => promptHash,
|
|
138
144
|
record: () => record,
|
|
145
|
+
registerPlugin: () => registerPlugin,
|
|
139
146
|
renderAssertionFailure: () => renderAssertionFailure,
|
|
140
147
|
renderDiagnostics: () => renderDiagnostics,
|
|
141
148
|
renderSummary: () => renderSummary,
|
|
149
|
+
renderVerdictDiagnostics: () => renderVerdictDiagnostics,
|
|
142
150
|
renderVitest: () => renderVitest,
|
|
151
|
+
repeat: () => repeat,
|
|
143
152
|
requireSimulationCapability: () => requireSimulationCapability,
|
|
144
153
|
resetConfig: () => resetConfig,
|
|
145
154
|
resolveMode: () => resolveMode,
|
|
155
|
+
scenario: () => scenario,
|
|
146
156
|
simulationEnvEnabled: () => simulationEnvEnabled,
|
|
147
157
|
startReplay: () => startReplay,
|
|
148
158
|
startWorld: () => startWorld,
|
|
@@ -377,8 +387,8 @@ function extractId(response) {
|
|
|
377
387
|
}
|
|
378
388
|
|
|
379
389
|
// src/version.ts
|
|
380
|
-
var VERSION = "1.0.0-rc.
|
|
381
|
-
var ENGINE_VERSION = "1.0.0-rc.
|
|
390
|
+
var VERSION = "1.0.0-rc.5";
|
|
391
|
+
var ENGINE_VERSION = "1.0.0-rc.5";
|
|
382
392
|
|
|
383
393
|
// src/engine-manager.ts
|
|
384
394
|
var import_node_child_process = require("child_process");
|
|
@@ -446,7 +456,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
446
456
|
new Error(`Too many redirects (>${MAX_REDIRECTS}) following ${url}`)
|
|
447
457
|
);
|
|
448
458
|
}
|
|
449
|
-
return new Promise((
|
|
459
|
+
return new Promise((resolve5, reject) => {
|
|
450
460
|
const client = url.startsWith("https:") ? https : http;
|
|
451
461
|
client.get(url, (res) => {
|
|
452
462
|
const status = res.statusCode ?? 0;
|
|
@@ -457,7 +467,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
457
467
|
return;
|
|
458
468
|
}
|
|
459
469
|
res.resume();
|
|
460
|
-
|
|
470
|
+
resolve5(httpsGet(location, redirectCount + 1));
|
|
461
471
|
return;
|
|
462
472
|
}
|
|
463
473
|
if (status < 200 || status >= 300) {
|
|
@@ -467,7 +477,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
467
477
|
}
|
|
468
478
|
const chunks = [];
|
|
469
479
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
470
|
-
res.on("end", () =>
|
|
480
|
+
res.on("end", () => resolve5(Buffer.concat(chunks)));
|
|
471
481
|
res.on("error", reject);
|
|
472
482
|
}).on("error", reject);
|
|
473
483
|
});
|
|
@@ -694,11 +704,11 @@ var EngineManager = class {
|
|
|
694
704
|
}
|
|
695
705
|
this.requestId += 1;
|
|
696
706
|
const requestStr = encodeRequest(this.requestId, method, params);
|
|
697
|
-
return new Promise((
|
|
707
|
+
return new Promise((resolve5, reject) => {
|
|
698
708
|
const onLine = (line) => {
|
|
699
709
|
try {
|
|
700
710
|
const response = decodeResponse(line);
|
|
701
|
-
|
|
711
|
+
resolve5(extractResult(response));
|
|
702
712
|
} catch (err) {
|
|
703
713
|
reject(err);
|
|
704
714
|
}
|
|
@@ -1065,12 +1075,12 @@ var AttestClient = class {
|
|
|
1065
1075
|
throw new Error("Engine process not started.");
|
|
1066
1076
|
}
|
|
1067
1077
|
let capturedReqId;
|
|
1068
|
-
const requestPromise = new Promise((
|
|
1078
|
+
const requestPromise = new Promise((resolve5, reject) => {
|
|
1069
1079
|
this.writeChain = this.writeChain.then(() => {
|
|
1070
1080
|
this.requestId += 1;
|
|
1071
1081
|
const reqId = this.requestId;
|
|
1072
1082
|
capturedReqId = reqId;
|
|
1073
|
-
this.pending.set(reqId, { resolve:
|
|
1083
|
+
this.pending.set(reqId, { resolve: resolve5, reject });
|
|
1074
1084
|
const requestStr = encodeRequest(reqId, method, params);
|
|
1075
1085
|
cp.stdin.write(requestStr, (err) => {
|
|
1076
1086
|
if (err) {
|
|
@@ -1185,6 +1195,26 @@ function requireSimulationCapability(init) {
|
|
|
1185
1195
|
|
|
1186
1196
|
// src/trace.ts
|
|
1187
1197
|
var import_node_crypto = require("crypto");
|
|
1198
|
+
|
|
1199
|
+
// src/simulation/context.ts
|
|
1200
|
+
var import_node_async_hooks = require("async_hooks");
|
|
1201
|
+
var activeMockRegistry = new import_node_async_hooks.AsyncLocalStorage();
|
|
1202
|
+
function createRng(seed) {
|
|
1203
|
+
if (seed === void 0) return Math.random;
|
|
1204
|
+
let state = seed >>> 0;
|
|
1205
|
+
return () => {
|
|
1206
|
+
state = state + 1831565813 >>> 0;
|
|
1207
|
+
let t = state;
|
|
1208
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
1209
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
1210
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
function sleep(ms) {
|
|
1214
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/trace.ts
|
|
1188
1218
|
var TraceBuilder = class {
|
|
1189
1219
|
traceId;
|
|
1190
1220
|
agentId;
|
|
@@ -1216,11 +1246,22 @@ var TraceBuilder = class {
|
|
|
1216
1246
|
return this;
|
|
1217
1247
|
}
|
|
1218
1248
|
addToolCall(name, options2) {
|
|
1249
|
+
const mock = activeMockRegistry.getStore()?.get(name);
|
|
1250
|
+
let result = options2?.result;
|
|
1251
|
+
if (mock !== void 0) {
|
|
1252
|
+
const mocked = mock(options2?.args ?? {});
|
|
1253
|
+
if (typeof mocked?.then === "function") {
|
|
1254
|
+
throw new Error(`mock for tool '${name}' returned a Promise; mock tools must be synchronous`);
|
|
1255
|
+
}
|
|
1256
|
+
if (typeof mocked === "object" && mocked !== null && !Array.isArray(mocked)) {
|
|
1257
|
+
result = mocked;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1219
1260
|
this.steps.push({
|
|
1220
1261
|
type: STEP_TOOL_CALL,
|
|
1221
1262
|
name,
|
|
1222
1263
|
args: options2?.args,
|
|
1223
|
-
result
|
|
1264
|
+
result,
|
|
1224
1265
|
metadata: options2?.metadata
|
|
1225
1266
|
});
|
|
1226
1267
|
return this;
|
|
@@ -2875,18 +2916,18 @@ function serve(engine, args) {
|
|
|
2875
2916
|
let stdout = "";
|
|
2876
2917
|
let stderr = "";
|
|
2877
2918
|
let resolveReady = () => void 0;
|
|
2878
|
-
const ready = new Promise((
|
|
2879
|
-
resolveReady =
|
|
2919
|
+
const ready = new Promise((resolve5) => {
|
|
2920
|
+
resolveReady = resolve5;
|
|
2880
2921
|
});
|
|
2881
|
-
const exited = new Promise((
|
|
2922
|
+
const exited = new Promise((resolve5) => {
|
|
2882
2923
|
child.on("close", (code, signal) => {
|
|
2883
2924
|
resolveReady(null);
|
|
2884
|
-
|
|
2925
|
+
resolve5({ code, signal });
|
|
2885
2926
|
});
|
|
2886
2927
|
child.on("error", (err) => {
|
|
2887
2928
|
stderr += err.message;
|
|
2888
2929
|
resolveReady(null);
|
|
2889
|
-
|
|
2930
|
+
resolve5({ code: null, signal: null });
|
|
2890
2931
|
});
|
|
2891
2932
|
});
|
|
2892
2933
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3143,18 +3184,18 @@ async function start(name, mode, opts) {
|
|
|
3143
3184
|
let stdout = "";
|
|
3144
3185
|
let stderr = "";
|
|
3145
3186
|
let resolveReady = () => void 0;
|
|
3146
|
-
const ready = new Promise((
|
|
3147
|
-
resolveReady =
|
|
3187
|
+
const ready = new Promise((resolve5) => {
|
|
3188
|
+
resolveReady = resolve5;
|
|
3148
3189
|
});
|
|
3149
|
-
const exited = new Promise((
|
|
3190
|
+
const exited = new Promise((resolve5) => {
|
|
3150
3191
|
child.on("close", (code) => {
|
|
3151
3192
|
resolveReady(null);
|
|
3152
|
-
|
|
3193
|
+
resolve5(code);
|
|
3153
3194
|
});
|
|
3154
3195
|
child.on("error", (err) => {
|
|
3155
3196
|
stderr += err.message;
|
|
3156
3197
|
resolveReady(null);
|
|
3157
|
-
|
|
3198
|
+
resolve5(null);
|
|
3158
3199
|
});
|
|
3159
3200
|
});
|
|
3160
3201
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3279,18 +3320,18 @@ async function start2(file, opts) {
|
|
|
3279
3320
|
let stdout = "";
|
|
3280
3321
|
let stderr = "";
|
|
3281
3322
|
let resolveReady = () => void 0;
|
|
3282
|
-
const ready = new Promise((
|
|
3283
|
-
resolveReady =
|
|
3323
|
+
const ready = new Promise((resolve5) => {
|
|
3324
|
+
resolveReady = resolve5;
|
|
3284
3325
|
});
|
|
3285
|
-
const exited = new Promise((
|
|
3326
|
+
const exited = new Promise((resolve5) => {
|
|
3286
3327
|
child.on("close", (code) => {
|
|
3287
3328
|
resolveReady(null);
|
|
3288
|
-
|
|
3329
|
+
resolve5(code);
|
|
3289
3330
|
});
|
|
3290
3331
|
child.on("error", (err) => {
|
|
3291
3332
|
stderr += err.message;
|
|
3292
3333
|
resolveReady(null);
|
|
3293
|
-
|
|
3334
|
+
resolve5(null);
|
|
3294
3335
|
});
|
|
3295
3336
|
});
|
|
3296
3337
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3371,8 +3412,8 @@ function parseFinal(out) {
|
|
|
3371
3412
|
}
|
|
3372
3413
|
|
|
3373
3414
|
// src/context.ts
|
|
3374
|
-
var
|
|
3375
|
-
var activeBuilder = new
|
|
3415
|
+
var import_node_async_hooks2 = require("async_hooks");
|
|
3416
|
+
var activeBuilder = new import_node_async_hooks2.AsyncLocalStorage();
|
|
3376
3417
|
|
|
3377
3418
|
// src/agent.ts
|
|
3378
3419
|
var Agent = class {
|
|
@@ -3459,6 +3500,9 @@ function tier(level) {
|
|
|
3459
3500
|
}
|
|
3460
3501
|
|
|
3461
3502
|
// src/plugins.ts
|
|
3503
|
+
var import_node_fs2 = require("fs");
|
|
3504
|
+
var import_node_path2 = require("path");
|
|
3505
|
+
var import_node_url2 = require("url");
|
|
3462
3506
|
var PluginRegistry = class {
|
|
3463
3507
|
plugins = /* @__PURE__ */ new Map();
|
|
3464
3508
|
register(name, plugin) {
|
|
@@ -3489,10 +3533,19 @@ var PluginRegistry = class {
|
|
|
3489
3533
|
};
|
|
3490
3534
|
async function executePluginAssertion(plugin, trace, spec, client, traceId2, assertionId2, timeoutMs = 3e4) {
|
|
3491
3535
|
const resultPromise = Promise.resolve(plugin.execute(trace, spec));
|
|
3536
|
+
let timer;
|
|
3492
3537
|
const timeoutPromise = new Promise((_, reject) => {
|
|
3493
|
-
|
|
3538
|
+
timer = setTimeout(
|
|
3539
|
+
() => reject(new Error(`Plugin '${plugin.name}' timed out after ${timeoutMs}ms`)),
|
|
3540
|
+
timeoutMs
|
|
3541
|
+
);
|
|
3494
3542
|
});
|
|
3495
|
-
|
|
3543
|
+
let result;
|
|
3544
|
+
try {
|
|
3545
|
+
result = await Promise.race([resultPromise, timeoutPromise]);
|
|
3546
|
+
} finally {
|
|
3547
|
+
clearTimeout(timer);
|
|
3548
|
+
}
|
|
3496
3549
|
await client.submitPluginResult(
|
|
3497
3550
|
traceId2,
|
|
3498
3551
|
plugin.name,
|
|
@@ -3503,6 +3556,100 @@ async function executePluginAssertion(plugin, trace, spec, client, traceId2, ass
|
|
|
3503
3556
|
);
|
|
3504
3557
|
return result;
|
|
3505
3558
|
}
|
|
3559
|
+
function registerPlugin(registry, name, plugin) {
|
|
3560
|
+
registry.register(name, plugin);
|
|
3561
|
+
}
|
|
3562
|
+
function isAttestPlugin(value) {
|
|
3563
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3564
|
+
const v = value;
|
|
3565
|
+
return typeof v["name"] === "string" && typeof v["pluginType"] === "string" && typeof v["execute"] === "function";
|
|
3566
|
+
}
|
|
3567
|
+
function readJson(path5) {
|
|
3568
|
+
return JSON.parse((0, import_node_fs2.readFileSync)(path5, "utf8"));
|
|
3569
|
+
}
|
|
3570
|
+
function findInstalledPackage(projectRoot, name) {
|
|
3571
|
+
let dir = (0, import_node_path2.resolve)(projectRoot);
|
|
3572
|
+
for (; ; ) {
|
|
3573
|
+
const candidate = (0, import_node_path2.join)(dir, "node_modules", name);
|
|
3574
|
+
if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(candidate, "package.json"))) return candidate;
|
|
3575
|
+
const parent = (0, import_node_path2.dirname)(dir);
|
|
3576
|
+
if (parent === dir) return void 0;
|
|
3577
|
+
dir = parent;
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
function declaredPluginPaths(pkgJson, dep) {
|
|
3581
|
+
const attest = pkgJson["attest"];
|
|
3582
|
+
if (attest === void 0) return [];
|
|
3583
|
+
const plugins = typeof attest === "object" && attest !== null ? attest["plugins"] : void 0;
|
|
3584
|
+
if (plugins === void 0) return [];
|
|
3585
|
+
if (!Array.isArray(plugins) || !plugins.every((p) => typeof p === "string")) {
|
|
3586
|
+
throw new Error(`'${dep}': "attest.plugins" must be an array of strings`);
|
|
3587
|
+
}
|
|
3588
|
+
return plugins;
|
|
3589
|
+
}
|
|
3590
|
+
function pluginsOf(mod) {
|
|
3591
|
+
const def = mod["default"];
|
|
3592
|
+
const nested = typeof def === "object" && def !== null ? def : void 0;
|
|
3593
|
+
for (const candidate of [def, nested?.["default"]]) {
|
|
3594
|
+
if (isAttestPlugin(candidate)) return [candidate];
|
|
3595
|
+
}
|
|
3596
|
+
return [.../* @__PURE__ */ new Set([...Object.values(mod), ...Object.values(nested ?? {})])].filter(isAttestPlugin);
|
|
3597
|
+
}
|
|
3598
|
+
function isInside(root, file) {
|
|
3599
|
+
const within = (r, f) => {
|
|
3600
|
+
const rel = (0, import_node_path2.relative)(r, f);
|
|
3601
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${import_node_path2.sep}`) && !(0, import_node_path2.isAbsolute)(rel);
|
|
3602
|
+
};
|
|
3603
|
+
return within(root, file) && within((0, import_node_fs2.realpathSync)(root), (0, import_node_fs2.realpathSync)(file));
|
|
3604
|
+
}
|
|
3605
|
+
async function loadPackagePlugins(registry, projectRoot = process.cwd()) {
|
|
3606
|
+
let project;
|
|
3607
|
+
try {
|
|
3608
|
+
project = readJson((0, import_node_path2.join)(projectRoot, "package.json"));
|
|
3609
|
+
} catch (err) {
|
|
3610
|
+
console.error(`attest: cannot read ${(0, import_node_path2.join)(projectRoot, "package.json")}; no plugins loaded:`, err);
|
|
3611
|
+
return 0;
|
|
3612
|
+
}
|
|
3613
|
+
const depNames = /* @__PURE__ */ new Set();
|
|
3614
|
+
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
3615
|
+
const deps = project[field];
|
|
3616
|
+
if (typeof deps === "object" && deps !== null) {
|
|
3617
|
+
for (const dep of Object.keys(deps)) depNames.add(dep);
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
let loaded = 0;
|
|
3621
|
+
for (const dep of depNames) {
|
|
3622
|
+
const pkgDir = findInstalledPackage(projectRoot, dep);
|
|
3623
|
+
if (pkgDir === void 0) continue;
|
|
3624
|
+
let entries;
|
|
3625
|
+
try {
|
|
3626
|
+
entries = declaredPluginPaths(readJson((0, import_node_path2.join)(pkgDir, "package.json")), dep);
|
|
3627
|
+
} catch (err) {
|
|
3628
|
+
console.error(`attest: failed to read plugins declared by '${dep}':`, err);
|
|
3629
|
+
continue;
|
|
3630
|
+
}
|
|
3631
|
+
for (const entry of entries) {
|
|
3632
|
+
try {
|
|
3633
|
+
const file = (0, import_node_path2.resolve)(pkgDir, entry);
|
|
3634
|
+
if (!isInside(pkgDir, file)) {
|
|
3635
|
+
throw new Error(`plugin path '${entry}' escapes package '${dep}'`);
|
|
3636
|
+
}
|
|
3637
|
+
const mod = await import((0, import_node_url2.pathToFileURL)(file).href);
|
|
3638
|
+
const plugins = pluginsOf(mod);
|
|
3639
|
+
if (plugins.length === 0) {
|
|
3640
|
+
throw new Error(`'${entry}' exports no plugin (name, pluginType, execute)`);
|
|
3641
|
+
}
|
|
3642
|
+
for (const plugin of plugins) {
|
|
3643
|
+
registry.register(plugin.name, plugin);
|
|
3644
|
+
loaded++;
|
|
3645
|
+
}
|
|
3646
|
+
} catch (err) {
|
|
3647
|
+
console.error(`attest: failed to load plugin '${entry}' from '${dep}':`, err);
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
return loaded;
|
|
3652
|
+
}
|
|
3506
3653
|
|
|
3507
3654
|
// src/continuous.ts
|
|
3508
3655
|
var import_node_events = require("events");
|
|
@@ -3882,6 +4029,172 @@ the assistant's response meets your needs. Keep responses concise (1-3 sentences
|
|
|
3882
4029
|
max_tokens: 200
|
|
3883
4030
|
};
|
|
3884
4031
|
|
|
4032
|
+
// src/simulation/mock-tools.ts
|
|
4033
|
+
var MockToolRegistry = class {
|
|
4034
|
+
tools = /* @__PURE__ */ new Map();
|
|
4035
|
+
register(name, fn) {
|
|
4036
|
+
this.tools.set(name, fn);
|
|
4037
|
+
}
|
|
4038
|
+
/**
|
|
4039
|
+
* Run `fn` with the registered mocks active. `TraceBuilder.addToolCall`
|
|
4040
|
+
* returns the mock's result for a registered tool name. Scopes are
|
|
4041
|
+
* isolated per async call chain (AsyncLocalStorage).
|
|
4042
|
+
*/
|
|
4043
|
+
run(fn) {
|
|
4044
|
+
return activeMockRegistry.run(new Map(this.tools), fn);
|
|
4045
|
+
}
|
|
4046
|
+
};
|
|
4047
|
+
function mockTool(name, fn) {
|
|
4048
|
+
const wrapper = ((args) => fn(args));
|
|
4049
|
+
return Object.assign(wrapper, { mockToolName: name });
|
|
4050
|
+
}
|
|
4051
|
+
|
|
4052
|
+
// src/simulation/fault-inject.ts
|
|
4053
|
+
function faultInject(options2, fn) {
|
|
4054
|
+
const errorRate = options2.errorRate ?? 0;
|
|
4055
|
+
const latencyJitterMs = options2.latencyJitterMs ?? 0;
|
|
4056
|
+
const rng = createRng(options2.seed);
|
|
4057
|
+
return async (...args) => {
|
|
4058
|
+
if (errorRate > 0 && rng() < errorRate) {
|
|
4059
|
+
throw new Error(`Injected fault in ${fn.name || "anonymous"}`);
|
|
4060
|
+
}
|
|
4061
|
+
if (latencyJitterMs > 0) {
|
|
4062
|
+
await sleep(rng() * latencyJitterMs);
|
|
4063
|
+
}
|
|
4064
|
+
return fn(...args);
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
// src/simulation/verdict.ts
|
|
4069
|
+
function aggregateOutcomes(outcomes) {
|
|
4070
|
+
if (outcomes.includes(OUTCOME_FAIL)) return OUTCOME_FAIL;
|
|
4071
|
+
if (outcomes.includes(OUTCOME_SKIP)) return OUTCOME_SKIP;
|
|
4072
|
+
return OUTCOME_PASS;
|
|
4073
|
+
}
|
|
4074
|
+
function sumNotEvaluatedReasons(results) {
|
|
4075
|
+
const counts = {};
|
|
4076
|
+
for (const r of results) {
|
|
4077
|
+
for (const [reason, n] of Object.entries(r.notEvaluatedReasons)) {
|
|
4078
|
+
counts[reason] = (counts[reason] ?? 0) + n;
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
return counts;
|
|
4082
|
+
}
|
|
4083
|
+
function formatSkipReason(counts) {
|
|
4084
|
+
const parts = Object.entries(counts).map(([reason, n]) => n === 1 ? reason : `${reason} \xD7${n}`);
|
|
4085
|
+
return `not evaluated: ${parts.join(", ")}`;
|
|
4086
|
+
}
|
|
4087
|
+
function renderVerdictDiagnostics(verdict, color = false) {
|
|
4088
|
+
return verdict instanceof AgentResult ? renderDiagnostics(verdict, { color }) : verdict.renderDiagnostics?.({ color }) ?? "";
|
|
4089
|
+
}
|
|
4090
|
+
function verdictsDiagnostics(results, label, color = false) {
|
|
4091
|
+
const blocks = results.flatMap(
|
|
4092
|
+
(r, i) => r.outcome() === OUTCOME_PASS ? [] : [`${label} ${i + 1}/${results.length}:${renderVerdictDiagnostics(r, color)}`]
|
|
4093
|
+
);
|
|
4094
|
+
return blocks.length > 0 ? blocks.join("\n") : ` (no failing ${label}s)
|
|
4095
|
+
`;
|
|
4096
|
+
}
|
|
4097
|
+
|
|
4098
|
+
// src/simulation/repeat.ts
|
|
4099
|
+
var RepeatResult = class {
|
|
4100
|
+
results;
|
|
4101
|
+
constructor(results = []) {
|
|
4102
|
+
this.results = results;
|
|
4103
|
+
}
|
|
4104
|
+
get count() {
|
|
4105
|
+
return this.results.length;
|
|
4106
|
+
}
|
|
4107
|
+
/** Trials skipped because an assertion was not evaluated. */
|
|
4108
|
+
get notEvaluated() {
|
|
4109
|
+
return this.results.filter((r) => r.outcome() === OUTCOME_SKIP).length;
|
|
4110
|
+
}
|
|
4111
|
+
/** Passing / evaluated trials; null when no trial was evaluated. */
|
|
4112
|
+
get passRate() {
|
|
4113
|
+
const evaluated = this.count - this.notEvaluated;
|
|
4114
|
+
if (evaluated === 0) return null;
|
|
4115
|
+
return this.results.filter((r) => r.outcome() === OUTCOME_PASS).length / evaluated;
|
|
4116
|
+
}
|
|
4117
|
+
/** True when every trial's outcome is pass (soft failures included). */
|
|
4118
|
+
get allPassed() {
|
|
4119
|
+
return this.results.every((r) => r.outcome() === OUTCOME_PASS);
|
|
4120
|
+
}
|
|
4121
|
+
outcome(options2) {
|
|
4122
|
+
return aggregateOutcomes(this.results.map((r) => r.outcome(options2)));
|
|
4123
|
+
}
|
|
4124
|
+
get notEvaluatedReasons() {
|
|
4125
|
+
return sumNotEvaluatedReasons(this.results);
|
|
4126
|
+
}
|
|
4127
|
+
skipReason() {
|
|
4128
|
+
return formatSkipReason(this.notEvaluatedReasons);
|
|
4129
|
+
}
|
|
4130
|
+
renderDiagnostics(options2) {
|
|
4131
|
+
return verdictsDiagnostics(this.results, "trial", options2?.color);
|
|
4132
|
+
}
|
|
4133
|
+
};
|
|
4134
|
+
function repeat(n, fn) {
|
|
4135
|
+
return async (...args) => {
|
|
4136
|
+
const results = [];
|
|
4137
|
+
for (let i = 0; i < n; i++) {
|
|
4138
|
+
results.push(await fn(...args));
|
|
4139
|
+
}
|
|
4140
|
+
return new RepeatResult(results);
|
|
4141
|
+
};
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
// src/simulation/scenario.ts
|
|
4145
|
+
var ScenarioResult = class {
|
|
4146
|
+
config;
|
|
4147
|
+
results;
|
|
4148
|
+
constructor(config2, results = []) {
|
|
4149
|
+
this.config = config2;
|
|
4150
|
+
this.results = results;
|
|
4151
|
+
}
|
|
4152
|
+
/** True when the aggregate `outcome()` is pass. */
|
|
4153
|
+
get passed() {
|
|
4154
|
+
return this.outcome() === OUTCOME_PASS;
|
|
4155
|
+
}
|
|
4156
|
+
outcome(options2) {
|
|
4157
|
+
return aggregateOutcomes(this.results.map((r) => r.outcome(options2)));
|
|
4158
|
+
}
|
|
4159
|
+
get notEvaluatedReasons() {
|
|
4160
|
+
return sumNotEvaluatedReasons(this.results);
|
|
4161
|
+
}
|
|
4162
|
+
skipReason() {
|
|
4163
|
+
return formatSkipReason(this.notEvaluatedReasons);
|
|
4164
|
+
}
|
|
4165
|
+
renderDiagnostics(options2) {
|
|
4166
|
+
return verdictsDiagnostics(this.results, "turn", options2?.color);
|
|
4167
|
+
}
|
|
4168
|
+
};
|
|
4169
|
+
function scenario(options2, fn) {
|
|
4170
|
+
const config2 = {
|
|
4171
|
+
persona: options2.persona,
|
|
4172
|
+
mockTools: { ...options2.mockTools },
|
|
4173
|
+
faultErrorRate: options2.faultErrorRate ?? 0,
|
|
4174
|
+
faultLatencyJitterMs: options2.faultLatencyJitterMs ?? 0,
|
|
4175
|
+
maxTurns: options2.maxTurns ?? 1,
|
|
4176
|
+
seed: options2.seed
|
|
4177
|
+
};
|
|
4178
|
+
return async (...args) => {
|
|
4179
|
+
const run = async () => {
|
|
4180
|
+
const results = [];
|
|
4181
|
+
const rng = createRng(config2.seed);
|
|
4182
|
+
for (let turn = 0; turn < config2.maxTurns; turn++) {
|
|
4183
|
+
if (config2.faultErrorRate > 0 && rng() < config2.faultErrorRate) {
|
|
4184
|
+
throw new Error(`Injected fault at turn ${turn}`);
|
|
4185
|
+
}
|
|
4186
|
+
if (config2.faultLatencyJitterMs > 0) {
|
|
4187
|
+
await sleep(rng() * config2.faultLatencyJitterMs);
|
|
4188
|
+
}
|
|
4189
|
+
results.push(await fn({ persona: config2.persona, turn }, ...args));
|
|
4190
|
+
}
|
|
4191
|
+
return new ScenarioResult(config2, results);
|
|
4192
|
+
};
|
|
4193
|
+
const mocks = new Map(Object.entries(config2.mockTools));
|
|
4194
|
+
return mocks.size > 0 ? activeMockRegistry.run(mocks, run) : run();
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
|
|
3885
4198
|
// src/adapters/manual.ts
|
|
3886
4199
|
var ManualAdapter = class {
|
|
3887
4200
|
agentId;
|
|
@@ -4093,7 +4406,7 @@ var OTelAdapter = class _OTelAdapter {
|
|
|
4093
4406
|
}
|
|
4094
4407
|
};
|
|
4095
4408
|
function runConvert(engine, args, payload) {
|
|
4096
|
-
return new Promise((
|
|
4409
|
+
return new Promise((resolve5, reject) => {
|
|
4097
4410
|
const child = (0, import_node_child_process5.spawn)(engine, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
4098
4411
|
let timer;
|
|
4099
4412
|
child.on("error", (e) => {
|
|
@@ -4146,7 +4459,7 @@ function runConvert(engine, args, payload) {
|
|
|
4146
4459
|
);
|
|
4147
4460
|
return;
|
|
4148
4461
|
}
|
|
4149
|
-
|
|
4462
|
+
resolve5(Buffer.concat(out).toString("utf8"));
|
|
4150
4463
|
});
|
|
4151
4464
|
stdin.end(payload);
|
|
4152
4465
|
});
|
|
@@ -4314,6 +4627,7 @@ var LangChainAdapter = class {
|
|
|
4314
4627
|
GeminiAdapter,
|
|
4315
4628
|
LangChainAdapter,
|
|
4316
4629
|
ManualAdapter,
|
|
4630
|
+
MockToolRegistry,
|
|
4317
4631
|
OTLPConversionError,
|
|
4318
4632
|
OTelAdapter,
|
|
4319
4633
|
OUTCOME_FAIL,
|
|
@@ -4333,6 +4647,7 @@ var LangChainAdapter = class {
|
|
|
4333
4647
|
REASON_REPLAY_MISS,
|
|
4334
4648
|
REASON_SHORT_CIRCUITED,
|
|
4335
4649
|
REASON_SIMULATION,
|
|
4650
|
+
RepeatResult,
|
|
4336
4651
|
ReplayError,
|
|
4337
4652
|
ReplayMissError,
|
|
4338
4653
|
ReplayNotEvaluatedError,
|
|
@@ -4345,6 +4660,7 @@ var LangChainAdapter = class {
|
|
|
4345
4660
|
STEP_RETRIEVAL,
|
|
4346
4661
|
STEP_TOOL_CALL,
|
|
4347
4662
|
Sampler,
|
|
4663
|
+
ScenarioResult,
|
|
4348
4664
|
TIER_1,
|
|
4349
4665
|
TIER_2,
|
|
4350
4666
|
TIER_3,
|
|
@@ -4379,6 +4695,7 @@ var LangChainAdapter = class {
|
|
|
4379
4695
|
executePluginAssertion,
|
|
4380
4696
|
extractId,
|
|
4381
4697
|
extractResult,
|
|
4698
|
+
faultInject,
|
|
4382
4699
|
firstMiss,
|
|
4383
4700
|
formatUsage,
|
|
4384
4701
|
getSampleRate,
|
|
@@ -4390,17 +4707,23 @@ var LangChainAdapter = class {
|
|
|
4390
4707
|
loadFixture,
|
|
4391
4708
|
loadLabelsCSV,
|
|
4392
4709
|
loadLabelsJSONL,
|
|
4710
|
+
loadPackagePlugins,
|
|
4711
|
+
mockTool,
|
|
4393
4712
|
parseUsage,
|
|
4394
4713
|
previewLine,
|
|
4395
4714
|
promptHash,
|
|
4396
4715
|
record,
|
|
4716
|
+
registerPlugin,
|
|
4397
4717
|
renderAssertionFailure,
|
|
4398
4718
|
renderDiagnostics,
|
|
4399
4719
|
renderSummary,
|
|
4720
|
+
renderVerdictDiagnostics,
|
|
4400
4721
|
renderVitest,
|
|
4722
|
+
repeat,
|
|
4401
4723
|
requireSimulationCapability,
|
|
4402
4724
|
resetConfig,
|
|
4403
4725
|
resolveMode,
|
|
4726
|
+
scenario,
|
|
4404
4727
|
simulationEnvEnabled,
|
|
4405
4728
|
startReplay,
|
|
4406
4729
|
startWorld,
|