@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/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) {
|
|
@@ -1095,7 +1105,8 @@ var AttestClient = class {
|
|
|
1095
1105
|
const params = {
|
|
1096
1106
|
trace,
|
|
1097
1107
|
assertions: [...assertions],
|
|
1098
|
-
...options2?.budget !== void 0 ? { budget: options2.budget } : {}
|
|
1108
|
+
...options2?.budget !== void 0 ? { budget: options2.budget } : {},
|
|
1109
|
+
...options2?.context !== void 0 ? { context: options2.context } : {}
|
|
1099
1110
|
};
|
|
1100
1111
|
const raw = await this.sendRequest(
|
|
1101
1112
|
"evaluate_batch",
|
|
@@ -1184,6 +1195,26 @@ function requireSimulationCapability(init) {
|
|
|
1184
1195
|
|
|
1185
1196
|
// src/trace.ts
|
|
1186
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
|
|
1187
1218
|
var TraceBuilder = class {
|
|
1188
1219
|
traceId;
|
|
1189
1220
|
agentId;
|
|
@@ -1215,11 +1246,22 @@ var TraceBuilder = class {
|
|
|
1215
1246
|
return this;
|
|
1216
1247
|
}
|
|
1217
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
|
+
}
|
|
1218
1260
|
this.steps.push({
|
|
1219
1261
|
type: STEP_TOOL_CALL,
|
|
1220
1262
|
name,
|
|
1221
1263
|
args: options2?.args,
|
|
1222
|
-
result
|
|
1264
|
+
result,
|
|
1223
1265
|
metadata: options2?.metadata
|
|
1224
1266
|
});
|
|
1225
1267
|
return this;
|
|
@@ -2874,18 +2916,18 @@ function serve(engine, args) {
|
|
|
2874
2916
|
let stdout = "";
|
|
2875
2917
|
let stderr = "";
|
|
2876
2918
|
let resolveReady = () => void 0;
|
|
2877
|
-
const ready = new Promise((
|
|
2878
|
-
resolveReady =
|
|
2919
|
+
const ready = new Promise((resolve5) => {
|
|
2920
|
+
resolveReady = resolve5;
|
|
2879
2921
|
});
|
|
2880
|
-
const exited = new Promise((
|
|
2922
|
+
const exited = new Promise((resolve5) => {
|
|
2881
2923
|
child.on("close", (code, signal) => {
|
|
2882
2924
|
resolveReady(null);
|
|
2883
|
-
|
|
2925
|
+
resolve5({ code, signal });
|
|
2884
2926
|
});
|
|
2885
2927
|
child.on("error", (err) => {
|
|
2886
2928
|
stderr += err.message;
|
|
2887
2929
|
resolveReady(null);
|
|
2888
|
-
|
|
2930
|
+
resolve5({ code: null, signal: null });
|
|
2889
2931
|
});
|
|
2890
2932
|
});
|
|
2891
2933
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3142,18 +3184,18 @@ async function start(name, mode, opts) {
|
|
|
3142
3184
|
let stdout = "";
|
|
3143
3185
|
let stderr = "";
|
|
3144
3186
|
let resolveReady = () => void 0;
|
|
3145
|
-
const ready = new Promise((
|
|
3146
|
-
resolveReady =
|
|
3187
|
+
const ready = new Promise((resolve5) => {
|
|
3188
|
+
resolveReady = resolve5;
|
|
3147
3189
|
});
|
|
3148
|
-
const exited = new Promise((
|
|
3190
|
+
const exited = new Promise((resolve5) => {
|
|
3149
3191
|
child.on("close", (code) => {
|
|
3150
3192
|
resolveReady(null);
|
|
3151
|
-
|
|
3193
|
+
resolve5(code);
|
|
3152
3194
|
});
|
|
3153
3195
|
child.on("error", (err) => {
|
|
3154
3196
|
stderr += err.message;
|
|
3155
3197
|
resolveReady(null);
|
|
3156
|
-
|
|
3198
|
+
resolve5(null);
|
|
3157
3199
|
});
|
|
3158
3200
|
});
|
|
3159
3201
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3278,18 +3320,18 @@ async function start2(file, opts) {
|
|
|
3278
3320
|
let stdout = "";
|
|
3279
3321
|
let stderr = "";
|
|
3280
3322
|
let resolveReady = () => void 0;
|
|
3281
|
-
const ready = new Promise((
|
|
3282
|
-
resolveReady =
|
|
3323
|
+
const ready = new Promise((resolve5) => {
|
|
3324
|
+
resolveReady = resolve5;
|
|
3283
3325
|
});
|
|
3284
|
-
const exited = new Promise((
|
|
3326
|
+
const exited = new Promise((resolve5) => {
|
|
3285
3327
|
child.on("close", (code) => {
|
|
3286
3328
|
resolveReady(null);
|
|
3287
|
-
|
|
3329
|
+
resolve5(code);
|
|
3288
3330
|
});
|
|
3289
3331
|
child.on("error", (err) => {
|
|
3290
3332
|
stderr += err.message;
|
|
3291
3333
|
resolveReady(null);
|
|
3292
|
-
|
|
3334
|
+
resolve5(null);
|
|
3293
3335
|
});
|
|
3294
3336
|
});
|
|
3295
3337
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -3370,8 +3412,8 @@ function parseFinal(out) {
|
|
|
3370
3412
|
}
|
|
3371
3413
|
|
|
3372
3414
|
// src/context.ts
|
|
3373
|
-
var
|
|
3374
|
-
var activeBuilder = new
|
|
3415
|
+
var import_node_async_hooks2 = require("async_hooks");
|
|
3416
|
+
var activeBuilder = new import_node_async_hooks2.AsyncLocalStorage();
|
|
3375
3417
|
|
|
3376
3418
|
// src/agent.ts
|
|
3377
3419
|
var Agent = class {
|
|
@@ -3458,6 +3500,9 @@ function tier(level) {
|
|
|
3458
3500
|
}
|
|
3459
3501
|
|
|
3460
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");
|
|
3461
3506
|
var PluginRegistry = class {
|
|
3462
3507
|
plugins = /* @__PURE__ */ new Map();
|
|
3463
3508
|
register(name, plugin) {
|
|
@@ -3488,10 +3533,19 @@ var PluginRegistry = class {
|
|
|
3488
3533
|
};
|
|
3489
3534
|
async function executePluginAssertion(plugin, trace, spec, client, traceId2, assertionId2, timeoutMs = 3e4) {
|
|
3490
3535
|
const resultPromise = Promise.resolve(plugin.execute(trace, spec));
|
|
3536
|
+
let timer;
|
|
3491
3537
|
const timeoutPromise = new Promise((_, reject) => {
|
|
3492
|
-
|
|
3538
|
+
timer = setTimeout(
|
|
3539
|
+
() => reject(new Error(`Plugin '${plugin.name}' timed out after ${timeoutMs}ms`)),
|
|
3540
|
+
timeoutMs
|
|
3541
|
+
);
|
|
3493
3542
|
});
|
|
3494
|
-
|
|
3543
|
+
let result;
|
|
3544
|
+
try {
|
|
3545
|
+
result = await Promise.race([resultPromise, timeoutPromise]);
|
|
3546
|
+
} finally {
|
|
3547
|
+
clearTimeout(timer);
|
|
3548
|
+
}
|
|
3495
3549
|
await client.submitPluginResult(
|
|
3496
3550
|
traceId2,
|
|
3497
3551
|
plugin.name,
|
|
@@ -3502,6 +3556,100 @@ async function executePluginAssertion(plugin, trace, spec, client, traceId2, ass
|
|
|
3502
3556
|
);
|
|
3503
3557
|
return result;
|
|
3504
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
|
+
}
|
|
3505
3653
|
|
|
3506
3654
|
// src/continuous.ts
|
|
3507
3655
|
var import_node_events = require("events");
|
|
@@ -3881,6 +4029,172 @@ the assistant's response meets your needs. Keep responses concise (1-3 sentences
|
|
|
3881
4029
|
max_tokens: 200
|
|
3882
4030
|
};
|
|
3883
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
|
+
|
|
3884
4198
|
// src/adapters/manual.ts
|
|
3885
4199
|
var ManualAdapter = class {
|
|
3886
4200
|
agentId;
|
|
@@ -4092,7 +4406,7 @@ var OTelAdapter = class _OTelAdapter {
|
|
|
4092
4406
|
}
|
|
4093
4407
|
};
|
|
4094
4408
|
function runConvert(engine, args, payload) {
|
|
4095
|
-
return new Promise((
|
|
4409
|
+
return new Promise((resolve5, reject) => {
|
|
4096
4410
|
const child = (0, import_node_child_process5.spawn)(engine, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
4097
4411
|
let timer;
|
|
4098
4412
|
child.on("error", (e) => {
|
|
@@ -4145,7 +4459,7 @@ function runConvert(engine, args, payload) {
|
|
|
4145
4459
|
);
|
|
4146
4460
|
return;
|
|
4147
4461
|
}
|
|
4148
|
-
|
|
4462
|
+
resolve5(Buffer.concat(out).toString("utf8"));
|
|
4149
4463
|
});
|
|
4150
4464
|
stdin.end(payload);
|
|
4151
4465
|
});
|
|
@@ -4313,6 +4627,7 @@ var LangChainAdapter = class {
|
|
|
4313
4627
|
GeminiAdapter,
|
|
4314
4628
|
LangChainAdapter,
|
|
4315
4629
|
ManualAdapter,
|
|
4630
|
+
MockToolRegistry,
|
|
4316
4631
|
OTLPConversionError,
|
|
4317
4632
|
OTelAdapter,
|
|
4318
4633
|
OUTCOME_FAIL,
|
|
@@ -4332,6 +4647,7 @@ var LangChainAdapter = class {
|
|
|
4332
4647
|
REASON_REPLAY_MISS,
|
|
4333
4648
|
REASON_SHORT_CIRCUITED,
|
|
4334
4649
|
REASON_SIMULATION,
|
|
4650
|
+
RepeatResult,
|
|
4335
4651
|
ReplayError,
|
|
4336
4652
|
ReplayMissError,
|
|
4337
4653
|
ReplayNotEvaluatedError,
|
|
@@ -4344,6 +4660,7 @@ var LangChainAdapter = class {
|
|
|
4344
4660
|
STEP_RETRIEVAL,
|
|
4345
4661
|
STEP_TOOL_CALL,
|
|
4346
4662
|
Sampler,
|
|
4663
|
+
ScenarioResult,
|
|
4347
4664
|
TIER_1,
|
|
4348
4665
|
TIER_2,
|
|
4349
4666
|
TIER_3,
|
|
@@ -4378,6 +4695,7 @@ var LangChainAdapter = class {
|
|
|
4378
4695
|
executePluginAssertion,
|
|
4379
4696
|
extractId,
|
|
4380
4697
|
extractResult,
|
|
4698
|
+
faultInject,
|
|
4381
4699
|
firstMiss,
|
|
4382
4700
|
formatUsage,
|
|
4383
4701
|
getSampleRate,
|
|
@@ -4389,17 +4707,23 @@ var LangChainAdapter = class {
|
|
|
4389
4707
|
loadFixture,
|
|
4390
4708
|
loadLabelsCSV,
|
|
4391
4709
|
loadLabelsJSONL,
|
|
4710
|
+
loadPackagePlugins,
|
|
4711
|
+
mockTool,
|
|
4392
4712
|
parseUsage,
|
|
4393
4713
|
previewLine,
|
|
4394
4714
|
promptHash,
|
|
4395
4715
|
record,
|
|
4716
|
+
registerPlugin,
|
|
4396
4717
|
renderAssertionFailure,
|
|
4397
4718
|
renderDiagnostics,
|
|
4398
4719
|
renderSummary,
|
|
4720
|
+
renderVerdictDiagnostics,
|
|
4399
4721
|
renderVitest,
|
|
4722
|
+
repeat,
|
|
4400
4723
|
requireSimulationCapability,
|
|
4401
4724
|
resetConfig,
|
|
4402
4725
|
resolveMode,
|
|
4726
|
+
scenario,
|
|
4403
4727
|
simulationEnvEnabled,
|
|
4404
4728
|
startReplay,
|
|
4405
4729
|
startWorld,
|